이벤트 분리 및 임의그리드 기능 추가
This commit is contained in:
parent
5ec615051d
commit
40a0af8e9b
@ -6,6 +6,7 @@ import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@
|
||||
import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
||||
import { onlyNumberInputChange } from '@/util/input-utils'
|
||||
import { fabric } from 'fabric'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
|
||||
const TYPE = {
|
||||
DOT: 'DOT',
|
||||
@ -16,6 +17,7 @@ export default function DotLineGrid(props) {
|
||||
// const [modalOption, setModalOption] = useRecoilState(modalState); //modal 열림닫힘 state
|
||||
const [close, setClose] = useState(false)
|
||||
const { setShowDotLineGridModal } = props
|
||||
const gridColor = useRecoilValue(gridColorState)
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState)
|
||||
@ -125,7 +127,7 @@ export default function DotLineGrid(props) {
|
||||
const horizontalLine = new fabric.Line(
|
||||
[0, i * verticalInterval - verticalInterval / 2, canvas.width, i * verticalInterval - verticalInterval / 2],
|
||||
{
|
||||
stroke: 'black',
|
||||
stroke: gridColor,
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
lockMovementX: true,
|
||||
|
||||
@ -1,559 +1,16 @@
|
||||
'use client'
|
||||
|
||||
import { useEffect, useRef } from 'react'
|
||||
import WithDraggable from '@/components/common/draggable/withDraggable'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { useEvent } from '@/hooks/useEvent'
|
||||
import {
|
||||
adsorptionPointAddModeState,
|
||||
adsorptionPointModeState,
|
||||
adsorptionRangeState,
|
||||
canvasHistoryState,
|
||||
canvasState,
|
||||
dotLineIntervalSelector,
|
||||
verticalHorizontalModeState,
|
||||
} from '@/store/canvasAtom'
|
||||
import {
|
||||
OUTER_LINE_TYPE,
|
||||
outerLineAngle1State,
|
||||
outerLineArrow1State,
|
||||
outerLineArrow2State,
|
||||
outerLineLength1State,
|
||||
outerLineLength2State,
|
||||
outerLinePointsState,
|
||||
outerLineTypeState,
|
||||
} from '@/store/outerLineAtom'
|
||||
import { useLine } from '@/hooks/useLine'
|
||||
import { distanceBetweenPoints } from '@/util/canvas-util'
|
||||
import { calculateAngle } from '@/util/qpolygon-utils'
|
||||
import { usePolygon } from '@/hooks/usePolygon'
|
||||
import { OUTER_LINE_TYPE } from '@/store/outerLineAtom'
|
||||
import { onlyNumberInputChange, onlyNumberWithDotInputChange } from '@/util/input-utils'
|
||||
import { useMouse } from '@/hooks/useMouse'
|
||||
import { useOuterLineWall } from '@/hooks/roofcover/useOuterLineWall'
|
||||
|
||||
export default function OuterLineWall(props) {
|
||||
const { setShowOutlineModal } = props
|
||||
const { getMessage } = useMessage()
|
||||
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
|
||||
useEvent()
|
||||
const { getIntersectMousePoint } = useMouse()
|
||||
const { addLine, removeLine } = useLine()
|
||||
|
||||
const { addPolygonByLines } = usePolygon()
|
||||
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
|
||||
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
|
||||
const adsorptionPointMode = useRecoilValue(adsorptionPointModeState)
|
||||
const adsorptionRange = useRecoilValue(adsorptionRangeState)
|
||||
const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격
|
||||
|
||||
const length1Ref = useRef(null)
|
||||
const length2Ref = useRef(null)
|
||||
const angle1Ref = useRef(null)
|
||||
const [length1, setLength1] = useRecoilState(outerLineLength1State)
|
||||
const [length2, setLength2] = useRecoilState(outerLineLength2State)
|
||||
const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
|
||||
const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
|
||||
const [points, setPoints] = useRecoilState(outerLinePointsState)
|
||||
const [type, setType] = useRecoilState(outerLineTypeState)
|
||||
const arrow1Ref = useRef(arrow1)
|
||||
const arrow2Ref = useRef(arrow2)
|
||||
|
||||
const isFix = useRef(false)
|
||||
|
||||
const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)
|
||||
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
|
||||
useEffect(() => {
|
||||
if (adsorptionPointAddMode) {
|
||||
return
|
||||
}
|
||||
removeMouseEvent('mouse:down', mouseDown)
|
||||
addCanvasMouseEventListener('mouse:down', mouseDown)
|
||||
clear()
|
||||
return () => {
|
||||
removeAllMouseEventListeners()
|
||||
}
|
||||
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval])
|
||||
|
||||
useEffect(() => {
|
||||
arrow1Ref.current = arrow1
|
||||
}, [arrow1])
|
||||
|
||||
useEffect(() => {
|
||||
arrow2Ref.current = arrow2
|
||||
}, [arrow2])
|
||||
|
||||
useEffect(() => {
|
||||
removeAllDocumentEventListeners()
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
clear()
|
||||
}, [type])
|
||||
|
||||
const clear = () => {
|
||||
setLength1(0)
|
||||
setLength2(0)
|
||||
|
||||
setArrow1('')
|
||||
setArrow2('')
|
||||
|
||||
setAngle1(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 }
|
||||
const length = distanceBetweenPoints(lastPoint, newPoint)
|
||||
if (verticalHorizontalMode) {
|
||||
const vector = {
|
||||
x: pointer.x - points[points.length - 1].x,
|
||||
y: pointer.y - points[points.length - 1].y,
|
||||
}
|
||||
const slope = Math.abs(vector.y / vector.x) // 기울기 계산
|
||||
|
||||
let scaledVector
|
||||
if (slope >= 1) {
|
||||
// 기울기가 1 이상이면 x축 방향으로 그림
|
||||
scaledVector = {
|
||||
x: 0,
|
||||
y: vector.y >= 0 ? Number(length) : -Number(length),
|
||||
}
|
||||
} else {
|
||||
// 기울기가 1 미만이면 y축 방향으로 그림
|
||||
scaledVector = {
|
||||
x: vector.x >= 0 ? Number(length) : -Number(length),
|
||||
y: 0,
|
||||
}
|
||||
}
|
||||
|
||||
const verticalLength = scaledVector.y
|
||||
const horizontalLength = scaledVector.x
|
||||
|
||||
newPoint = {
|
||||
x: lastPoint.x + horizontalLength,
|
||||
y: lastPoint.y + verticalLength,
|
||||
}
|
||||
}
|
||||
setPoints((prev) => [...prev, newPoint])
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
canvas
|
||||
?.getObjects()
|
||||
.filter((obj) => obj.name === 'outerLine' || obj.name === 'helpGuideLine')
|
||||
.forEach((obj) => {
|
||||
removeLine(obj)
|
||||
})
|
||||
|
||||
canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint'))
|
||||
|
||||
// point가 변경 될때마다 이벤트 리스너를 제거하고 다시 등록
|
||||
removeAllDocumentEventListeners()
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
|
||||
if (points.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
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: 'startPoint',
|
||||
})
|
||||
|
||||
canvas?.add(point)
|
||||
} else {
|
||||
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 (points.length < 3) {
|
||||
return
|
||||
}
|
||||
|
||||
/*if (lastPoint.x === firstPoint.x && lastPoint.y === firstPoint.y) {
|
||||
return
|
||||
}
|
||||
|
||||
if (lastPoint.x === firstPoint.x || lastPoint.y === firstPoint.y) {
|
||||
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 = new QLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], {
|
||||
stroke: 'grey',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'helpGuideLine',
|
||||
})
|
||||
|
||||
canvas?.add(line)
|
||||
addLineText(line)
|
||||
} else {
|
||||
const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], {
|
||||
stroke: 'grey',
|
||||
strokeWidth: 1,
|
||||
strokeDashArray: [1, 1, 1],
|
||||
name: 'helpGuideLine',
|
||||
})
|
||||
|
||||
const guideLine2 = new QLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], {
|
||||
stroke: 'grey',
|
||||
strokeWidth: 1,
|
||||
strokeDashArray: [1, 1, 1],
|
||||
name: 'helpGuideLine',
|
||||
})
|
||||
if (guideLine1.length > 0) {
|
||||
canvas?.add(guideLine1)
|
||||
addLineText(guideLine1)
|
||||
}
|
||||
|
||||
canvas?.add(guideLine2)
|
||||
|
||||
addLineText(guideLine2)
|
||||
}*/
|
||||
}
|
||||
}, [points])
|
||||
|
||||
const drawLine = (point1, point2, idx) => {
|
||||
addLine([point1.x, point1.y, point2.x, point2.y], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
idx: idx,
|
||||
selectable: false,
|
||||
name: 'outerLine',
|
||||
})
|
||||
}
|
||||
|
||||
const settingLine = () => {
|
||||
const outerLines = canvas?.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
outerLines.forEach((line) => {
|
||||
removeLine(line)
|
||||
})
|
||||
|
||||
addPolygonByLines(outerLines, {
|
||||
fill: 'rgba(0,0,0,0)',
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
setShowOutlineModal(false)
|
||||
}
|
||||
|
||||
// 직각 완료될 경우 확인
|
||||
const checkRightAngle = () => {
|
||||
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 keydown = {
|
||||
outerLine: (e) => {
|
||||
if (points.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
|
||||
}
|
||||
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
|
||||
}
|
||||
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': {
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('↓')
|
||||
arrow1Ref.current = '↓'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') {
|
||||
break
|
||||
}
|
||||
setArrow2('↓')
|
||||
arrow2Ref.current = '↓'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
case 'Up': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowUp':
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('↑')
|
||||
arrow1Ref.current = '↑'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') {
|
||||
break
|
||||
}
|
||||
setArrow2('↑')
|
||||
arrow2Ref.current = '↑'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
break
|
||||
case 'Left': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowLeft':
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('←')
|
||||
arrow1Ref.current = '←'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '←' || arrow1Ref.current === '→') {
|
||||
break
|
||||
}
|
||||
setArrow2('←')
|
||||
arrow2Ref.current = '←'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
break
|
||||
case 'Right': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowRight':
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('→')
|
||||
arrow1Ref.current = '→'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '←' || arrow1Ref.current === '→') {
|
||||
break
|
||||
}
|
||||
setArrow2('→')
|
||||
arrow2Ref.current = '→'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
},
|
||||
leeGubae: (e) => {
|
||||
console.log('leegubae')
|
||||
},
|
||||
angle: (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) => {
|
||||
console.log('diagonalLine')
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* 일변전으로 돌아가기
|
||||
*/
|
||||
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 { points, length1, setLength1, length2, setLength2, length1Ref, length2Ref, arrow1, arrow2, type, setType, handleFix, handleRollback } =
|
||||
useOuterLineWall()
|
||||
|
||||
return (
|
||||
<WithDraggable isShow={true} pos={{ x: -1390, y: 30 }}>
|
||||
|
||||
@ -1,27 +1,65 @@
|
||||
import React from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import { useRecoilState } from 'recoil'
|
||||
import { settingModalGridOptionsState } from '@/store/settingAtom'
|
||||
import { useMessage } from '@/hooks/useMessage'
|
||||
import { adsorptionPointAddModeState } from '@/store/canvasAtom'
|
||||
import { useTempGrid } from '@/hooks/useTempGrid'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
import { useColor } from 'react-color-palette'
|
||||
|
||||
export default function GridOption(props) {
|
||||
const { setShowDotLineGridModal } = props
|
||||
const [gridOptions, setGridOptions] = useRecoilState(settingModalGridOptionsState)
|
||||
const [gridColor, setGridColor] = useRecoilState(gridColorState)
|
||||
const [adsorptionPointAddMode, setAdsorptionPointAddMode] = useRecoilState(adsorptionPointAddModeState)
|
||||
const { getMessage } = useMessage()
|
||||
const { tempGridMode, setTempGridMode } = useTempGrid()
|
||||
|
||||
const [color, setColor] = useColor(gridColor)
|
||||
|
||||
useEffect(() => {
|
||||
console.log(color)
|
||||
setGridColor(color.hex)
|
||||
}, [color])
|
||||
|
||||
const onClickOption = (option) => {
|
||||
option.selected = !option.selected
|
||||
setGridOptions([...gridOptions])
|
||||
const newGridOptions = [...gridOptions]
|
||||
newGridOptions.map((item) => {
|
||||
if (item.id === option.id) {
|
||||
item.selected = !item.selected
|
||||
}
|
||||
})
|
||||
|
||||
if (option.id === 1) {
|
||||
// 점 그리드
|
||||
setAdsorptionPointAddMode(false)
|
||||
setTempGridMode(option.selected)
|
||||
newGridOptions[2].selected = false
|
||||
}
|
||||
|
||||
if (option.id === 2) {
|
||||
// 점.선 그리드
|
||||
setShowDotLineGridModal(true)
|
||||
if (option.selected) {
|
||||
setShowDotLineGridModal(true)
|
||||
} else {
|
||||
setShowDotLineGridModal(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (option.name === 'modal.canvas.setting.grid.absorption.add') {
|
||||
setAdsorptionPointAddMode(!adsorptionPointAddMode)
|
||||
setTempGridMode(false)
|
||||
newGridOptions[0].selected = false
|
||||
}
|
||||
|
||||
if (option.id === 4) {
|
||||
// 그리드 색 설정
|
||||
if (option.selected) {
|
||||
setColorPickerShow(true)
|
||||
}
|
||||
}
|
||||
|
||||
setGridOptions(newGridOptions)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
566
src/hooks/roofcover/useOuterLineWall.js
Normal file
566
src/hooks/roofcover/useOuterLineWall.js
Normal file
@ -0,0 +1,566 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { distanceBetweenPoints } from '@/util/canvas-util'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import {
|
||||
adsorptionPointAddModeState,
|
||||
adsorptionPointModeState,
|
||||
adsorptionRangeState,
|
||||
canvasState,
|
||||
dotLineIntervalSelector,
|
||||
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 { usePolygon } from '@/hooks/usePolygon'
|
||||
import {
|
||||
outerLineAngle1State,
|
||||
outerLineArrow1State,
|
||||
outerLineArrow2State,
|
||||
outerLineLength1State,
|
||||
outerLineLength2State,
|
||||
outerLinePointsState,
|
||||
outerLineTypeState,
|
||||
} from '@/store/outerLineAtom'
|
||||
import { calculateAngle } from '@/util/qpolygon-utils'
|
||||
|
||||
export function useOuterLineWall() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } =
|
||||
useEvent()
|
||||
const { getIntersectMousePoint } = useMouse()
|
||||
const { addLine, removeLine } = useLine()
|
||||
const { tempGridMode } = useTempGrid()
|
||||
const { addPolygonByLines } = usePolygon()
|
||||
const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState)
|
||||
const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState)
|
||||
const adsorptionPointMode = useRecoilValue(adsorptionPointModeState)
|
||||
const adsorptionRange = useRecoilValue(adsorptionRangeState)
|
||||
const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격
|
||||
|
||||
const length1Ref = useRef(null)
|
||||
const length2Ref = useRef(null)
|
||||
const angle1Ref = useRef(null)
|
||||
const [length1, setLength1] = useRecoilState(outerLineLength1State)
|
||||
const [length2, setLength2] = useRecoilState(outerLineLength2State)
|
||||
const [arrow1, setArrow1] = useRecoilState(outerLineArrow1State)
|
||||
const [arrow2, setArrow2] = useRecoilState(outerLineArrow2State)
|
||||
const [points, setPoints] = useRecoilState(outerLinePointsState)
|
||||
const [type, setType] = useRecoilState(outerLineTypeState)
|
||||
const arrow1Ref = useRef(arrow1)
|
||||
const arrow2Ref = useRef(arrow2)
|
||||
|
||||
const isFix = useRef(false)
|
||||
|
||||
const [angle1, setAngle1] = useRecoilState(outerLineAngle1State)
|
||||
useEffect(() => {
|
||||
if (adsorptionPointAddMode || tempGridMode) {
|
||||
return
|
||||
}
|
||||
removeMouseEvent('mouse:down', mouseDown)
|
||||
addCanvasMouseEventListener('mouse:down', mouseDown)
|
||||
clear()
|
||||
return () => {
|
||||
removeAllMouseEventListeners()
|
||||
}
|
||||
}, [verticalHorizontalMode, points, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, interval, tempGridMode])
|
||||
|
||||
useEffect(() => {
|
||||
arrow1Ref.current = arrow1
|
||||
}, [arrow1])
|
||||
|
||||
useEffect(() => {
|
||||
arrow2Ref.current = arrow2
|
||||
}, [arrow2])
|
||||
|
||||
useEffect(() => {
|
||||
removeAllDocumentEventListeners()
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
clear()
|
||||
}, [type])
|
||||
|
||||
const clear = () => {
|
||||
setLength1(0)
|
||||
setLength2(0)
|
||||
|
||||
setArrow1('')
|
||||
setArrow2('')
|
||||
|
||||
setAngle1(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 }
|
||||
const length = distanceBetweenPoints(lastPoint, newPoint)
|
||||
if (verticalHorizontalMode) {
|
||||
const vector = {
|
||||
x: pointer.x - points[points.length - 1].x,
|
||||
y: pointer.y - points[points.length - 1].y,
|
||||
}
|
||||
const slope = Math.abs(vector.y / vector.x) // 기울기 계산
|
||||
|
||||
let scaledVector
|
||||
if (slope >= 1) {
|
||||
// 기울기가 1 이상이면 x축 방향으로 그림
|
||||
scaledVector = {
|
||||
x: 0,
|
||||
y: vector.y >= 0 ? Number(length) : -Number(length),
|
||||
}
|
||||
} else {
|
||||
// 기울기가 1 미만이면 y축 방향으로 그림
|
||||
scaledVector = {
|
||||
x: vector.x >= 0 ? Number(length) : -Number(length),
|
||||
y: 0,
|
||||
}
|
||||
}
|
||||
|
||||
const verticalLength = scaledVector.y
|
||||
const horizontalLength = scaledVector.x
|
||||
|
||||
newPoint = {
|
||||
x: lastPoint.x + horizontalLength,
|
||||
y: lastPoint.y + verticalLength,
|
||||
}
|
||||
}
|
||||
setPoints((prev) => [...prev, newPoint])
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
canvas
|
||||
?.getObjects()
|
||||
.filter((obj) => obj.name === 'outerLine' || obj.name === 'helpGuideLine')
|
||||
.forEach((obj) => {
|
||||
removeLine(obj)
|
||||
})
|
||||
|
||||
canvas?.remove(canvas?.getObjects().find((obj) => obj.name === 'startPoint'))
|
||||
|
||||
// point가 변경 될때마다 이벤트 리스너를 제거하고 다시 등록
|
||||
removeAllDocumentEventListeners()
|
||||
addDocumentEventListener('keydown', document, keydown[type])
|
||||
|
||||
if (points.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
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: 'startPoint',
|
||||
})
|
||||
|
||||
canvas?.add(point)
|
||||
} else {
|
||||
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 (points.length < 3) {
|
||||
return
|
||||
}
|
||||
|
||||
/*if (lastPoint.x === firstPoint.x && lastPoint.y === firstPoint.y) {
|
||||
return
|
||||
}
|
||||
|
||||
if (lastPoint.x === firstPoint.x || lastPoint.y === firstPoint.y) {
|
||||
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 = new QLine([lastPoint.x, lastPoint.y, firstPoint.x, firstPoint.y], {
|
||||
stroke: 'grey',
|
||||
strokeWidth: 1,
|
||||
selectable: false,
|
||||
name: 'helpGuideLine',
|
||||
})
|
||||
|
||||
canvas?.add(line)
|
||||
addLineText(line)
|
||||
} else {
|
||||
const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], {
|
||||
stroke: 'grey',
|
||||
strokeWidth: 1,
|
||||
strokeDashArray: [1, 1, 1],
|
||||
name: 'helpGuideLine',
|
||||
})
|
||||
|
||||
const guideLine2 = new QLine([guideLine1.x2, guideLine1.y2, firstPoint.x, firstPoint.y], {
|
||||
stroke: 'grey',
|
||||
strokeWidth: 1,
|
||||
strokeDashArray: [1, 1, 1],
|
||||
name: 'helpGuideLine',
|
||||
})
|
||||
if (guideLine1.length > 0) {
|
||||
canvas?.add(guideLine1)
|
||||
addLineText(guideLine1)
|
||||
}
|
||||
|
||||
canvas?.add(guideLine2)
|
||||
|
||||
addLineText(guideLine2)
|
||||
}*/
|
||||
}
|
||||
}, [points])
|
||||
|
||||
const drawLine = (point1, point2, idx) => {
|
||||
addLine([point1.x, point1.y, point2.x, point2.y], {
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
idx: idx,
|
||||
selectable: false,
|
||||
name: 'outerLine',
|
||||
})
|
||||
}
|
||||
|
||||
const settingLine = () => {
|
||||
const outerLines = canvas?.getObjects().filter((obj) => obj.name === 'outerLine')
|
||||
outerLines.forEach((line) => {
|
||||
removeLine(line)
|
||||
})
|
||||
|
||||
addPolygonByLines(outerLines, {
|
||||
fill: 'rgba(0,0,0,0)',
|
||||
stroke: 'black',
|
||||
strokeWidth: 3,
|
||||
})
|
||||
setShowOutlineModal(false)
|
||||
}
|
||||
|
||||
// 직각 완료될 경우 확인
|
||||
const checkRightAngle = () => {
|
||||
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 keydown = {
|
||||
outerLine: (e) => {
|
||||
if (points.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
|
||||
}
|
||||
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
|
||||
}
|
||||
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': {
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('↓')
|
||||
arrow1Ref.current = '↓'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') {
|
||||
break
|
||||
}
|
||||
setArrow2('↓')
|
||||
arrow2Ref.current = '↓'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
case 'Up': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowUp':
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('↑')
|
||||
arrow1Ref.current = '↑'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '↓' || arrow1Ref.current === '↑') {
|
||||
break
|
||||
}
|
||||
setArrow2('↑')
|
||||
arrow2Ref.current = '↑'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
break
|
||||
case 'Left': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowLeft':
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('←')
|
||||
arrow1Ref.current = '←'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '←' || arrow1Ref.current === '→') {
|
||||
break
|
||||
}
|
||||
setArrow2('←')
|
||||
arrow2Ref.current = '←'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
break
|
||||
case 'Right': // IE/Edge에서 사용되는 값
|
||||
case 'ArrowRight':
|
||||
if (activeElem === length1Ref.current) {
|
||||
setArrow1('→')
|
||||
arrow1Ref.current = '→'
|
||||
length2Ref.current.focus()
|
||||
} else if (activeElem === length2Ref.current) {
|
||||
if (arrow1Ref.current === '←' || arrow1Ref.current === '→') {
|
||||
break
|
||||
}
|
||||
setArrow2('→')
|
||||
arrow2Ref.current = '→'
|
||||
checkRightAngle()
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
},
|
||||
leeGubae: (e) => {
|
||||
console.log('leegubae')
|
||||
},
|
||||
angle: (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) => {
|
||||
console.log('diagonalLine')
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* 일변전으로 돌아가기
|
||||
*/
|
||||
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 }]
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
points,
|
||||
setPoints,
|
||||
length1,
|
||||
setLength1,
|
||||
length2,
|
||||
setLength2,
|
||||
length1Ref,
|
||||
length2Ref,
|
||||
arrow1,
|
||||
setArrow1,
|
||||
arrow2,
|
||||
setArrow2,
|
||||
arrow1Ref,
|
||||
arrow2Ref,
|
||||
type,
|
||||
setType,
|
||||
handleFix,
|
||||
handleRollback,
|
||||
}
|
||||
}
|
||||
@ -2,9 +2,11 @@ import { useRecoilState, useRecoilValue, useResetRecoilState } from 'recoil'
|
||||
import { canvasState, dotLineGridSettingState, dotLineIntervalSelector } from '@/store/canvasAtom'
|
||||
import { calculateDistance } from '@/util/canvas-util'
|
||||
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
|
||||
export function useDotLineGrid() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const gridColor = useRecoilValue(gridColorState)
|
||||
const [dotLineGridSetting, setDotLineGridSettingState] = useRecoilState(dotLineGridSettingState)
|
||||
const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격
|
||||
|
||||
@ -13,7 +15,7 @@ export function useDotLineGrid() {
|
||||
const resetDotLineGridSetting = useResetRecoilState(dotLineGridSettingState)
|
||||
|
||||
const getLineGrids = () => {
|
||||
return canvas.getObjects().filter((obj) => obj.name === 'lineGrid')
|
||||
return canvas.getObjects().filter((obj) => obj.name === 'lineGrid' || obj.name === 'tempGrid')
|
||||
}
|
||||
|
||||
const getClosestLineGrid = (point) => {
|
||||
|
||||
@ -13,6 +13,7 @@ import { calculateDistance, calculateIntersection, distanceBetweenPoints, findCl
|
||||
import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint'
|
||||
import { useMouse } from '@/hooks/useMouse'
|
||||
import { useDotLineGrid } from '@/hooks/useDotLineGrid'
|
||||
import { useTempGrid } from '@/hooks/useTempGrid'
|
||||
|
||||
export function useEvent() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
@ -23,6 +24,8 @@ export function useEvent() {
|
||||
|
||||
const { adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, getAdsorptionPoints, adsorptionPointAddModeStateEvent } = useAdsorptionPoint()
|
||||
const { dotLineGridSetting, interval, getClosestLineGrid } = useDotLineGrid()
|
||||
const { tempGridModeStateLeftClickEvent, tempGridMode, tempGridRightClickEvent } = useTempGrid()
|
||||
|
||||
useEffect(() => {
|
||||
if (!canvas) {
|
||||
return
|
||||
@ -37,17 +40,20 @@ export function useEvent() {
|
||||
canvas?.on('mouse:wheel', wheelEvent)
|
||||
|
||||
addDefaultEvent()
|
||||
}, [currentMenu, canvas, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, dotLineGridSetting])
|
||||
}, [currentMenu, canvas, adsorptionPointAddMode, adsorptionPointMode, adsorptionRange, dotLineGridSetting, tempGridMode])
|
||||
|
||||
const addDefaultEvent = () => {
|
||||
//default Event 추가
|
||||
addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent)
|
||||
addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent)
|
||||
addDocumentEventListener('keydown', document, defaultKeyboardEvent)
|
||||
if (adsorptionPointAddMode) {
|
||||
addCanvasMouseEventListener('mouse:down', adsorptionPointAddModeStateEvent)
|
||||
}
|
||||
addDocumentEventListener('keydown', document, defaultKeyboardEvent)
|
||||
addDocumentEventListener('contextmenu', document, defaultContextMenuEvent)
|
||||
if (tempGridMode) {
|
||||
addCanvasMouseEventListener('mouse:down', tempGridModeStateLeftClickEvent)
|
||||
addDocumentEventListener('contextmenu', document, tempGridRightClickEvent)
|
||||
}
|
||||
}
|
||||
|
||||
const defaultContextMenuEvent = (e) => {
|
||||
@ -89,7 +95,7 @@ export function useEvent() {
|
||||
let arrivalPoint = { x: pointer.x, y: pointer.y }
|
||||
|
||||
if (adsorptionPointMode) {
|
||||
if (dotLineGridSetting.LINE) {
|
||||
if (dotLineGridSetting.LINE || canvas.getObjects().filter((obj) => ['lineGrid', 'tempGrid'].includes(obj.name)).length > 0) {
|
||||
const closestLine = getClosestLineGrid(pointer)
|
||||
|
||||
if (closestLine) {
|
||||
|
||||
65
src/hooks/useTempGrid.js
Normal file
65
src/hooks/useTempGrid.js
Normal file
@ -0,0 +1,65 @@
|
||||
import { canvasState, tempGridModeState } from '@/store/canvasAtom'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import { gridColorState } from '@/store/gridAtom'
|
||||
|
||||
export function useTempGrid() {
|
||||
const canvas = useRecoilValue(canvasState)
|
||||
const gridColor = useRecoilValue(gridColorState)
|
||||
const [tempGridMode, setTempGridMode] = useRecoilState(tempGridModeState)
|
||||
const tempGridModeStateLeftClickEvent = (e) => {
|
||||
//임의 그리드 모드일 경우
|
||||
let pointer = canvas.getPointer(e.e)
|
||||
|
||||
const tempGrid = new fabric.Line([pointer.x, 0, pointer.x, canvas.height], {
|
||||
stroke: gridColor,
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
lockMovementX: true,
|
||||
lockMovementY: true,
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
strokeDashArray: [5, 2],
|
||||
opacity: 0.3,
|
||||
direction: 'vertical',
|
||||
name: 'tempGrid',
|
||||
})
|
||||
|
||||
canvas.add(tempGrid)
|
||||
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
const tempGridRightClickEvent = (e) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
//임의 그리드 모드일 경우
|
||||
let pointer = { x: e.offsetX, y: e.offsetY }
|
||||
|
||||
const tempGrid = new fabric.Line([0, pointer.y, canvas.width, pointer.y], {
|
||||
stroke: gridColor,
|
||||
strokeWidth: 1,
|
||||
selectable: true,
|
||||
lockMovementX: true,
|
||||
lockMovementY: true,
|
||||
lockRotation: true,
|
||||
lockScalingX: true,
|
||||
lockScalingY: true,
|
||||
strokeDashArray: [5, 2],
|
||||
opacity: 0.3,
|
||||
name: 'tempGrid',
|
||||
direction: 'horizontal',
|
||||
})
|
||||
|
||||
canvas.add(tempGrid)
|
||||
|
||||
canvas.renderAll()
|
||||
}
|
||||
|
||||
return {
|
||||
tempGridModeStateLeftClickEvent,
|
||||
tempGridRightClickEvent,
|
||||
tempGridMode,
|
||||
setTempGridMode,
|
||||
}
|
||||
}
|
||||
@ -262,3 +262,8 @@ export const currentCanvasPlanState = atom({
|
||||
key: 'currentCanvasPlan',
|
||||
default: {},
|
||||
})
|
||||
|
||||
export const tempGridModeState = atom({
|
||||
key: 'tempGridModeState',
|
||||
default: false,
|
||||
})
|
||||
|
||||
6
src/store/gridAtom.js
Normal file
6
src/store/gridAtom.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { atom } from 'recoil'
|
||||
|
||||
export const gridColorState = atom({
|
||||
key: 'gridColorState',
|
||||
default: '#000000',
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user