import { canvasState, currentObjectState } from '@/store/canvasAtom' import { useRecoilValue } from 'recoil' import { useEffect, useRef, useState } from 'react' import { useMessage } from '@/hooks/useMessage' import { useEvent } from '@/hooks/useEvent' import { useLine } from '@/hooks/useLine' import { useSwal } from '@/hooks/useSwal' import { usePopup } from '@/hooks/usePopup' import Big from 'big.js' import { outerLineFixState } from '@/store/outerLineAtom' // 외벽선 편집 및 오프셋 export function useWallLineOffsetSetting(id) { const canvas = useRecoilValue(canvasState) const { showLine, addLine } = useLine() const { getMessage } = useMessage() const { closePopup } = usePopup() const { swalFire } = useSwal() const { addCanvasMouseEventListener, initEvent } = useEvent() // const { addCanvasMouseEventListener, initEvent } = useContext(EventContext) const wallLineEditRef = useRef(null) const length1Ref = useRef(null) const length2Ref = useRef(null) const radioTypeRef = useRef('1') const currentWallLineRef = useRef(null) const arrow1Ref = useRef(null) const arrow2Ref = useRef(null) const currentObject = useRecoilValue(currentObjectState) const [isLoading, setIsLoading] = useState(false) const outerLineFix = useRecoilValue(outerLineFixState) const drawLine = (point1, point2, idx, direction = currentWallLineRef.current.direction) => { const line = addLine([point1.x, point1.y, point2.x, point2.y], { stroke: 'black', strokeWidth: 4, idx: idx, selectable: true, name: 'outerLine', x1: point1.x, y1: point1.y, x2: point2.x, y2: point2.y, direction: direction, }) line.attributes = { ...currentWallLineRef.current.attributes } } const TYPES = { WALL_LINE_EDIT: 'wallLineEdit', OFFSET: 'offset', } const buttonMenu = [ { id: 1, name: getMessage('modal.wallline.offset.setting.wallline.edit'), type: TYPES.WALL_LINE_EDIT }, { id: 2, name: getMessage('modal.wallline.offset.setting.offset'), type: TYPES.OFFSET }, ] const [type, setType] = useState(TYPES.WALL_LINE_EDIT) useEffect(() => { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') if (outerLines.length === 0) { swalFire({ text: getMessage('wall.line.not.found') }) closePopup(id) return } outerLines.forEach((outerLine) => { outerLine.set({ selectable: true }) showLine(outerLine) }) //outerLine과 그 text만 남겨둔다. const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine') exceptObjs.forEach((obj) => { canvas.remove(obj) }) canvas.setActiveObject(outerLines[0]) currentWallLineRef.current = outerLines[0] addCircleByLine(currentWallLineRef.current) addCanvasMouseEventListener('mouse:down', mouseDown) setIsLoading(true) return () => { removeOuterLineEditCircle() canvas.discardActiveObject() initEvent() } }, []) useEffect(() => { if (!isLoading) { return } canvas?.discardActiveObject() removeOuterLineEditCircle() addCanvasMouseEventListener('mouse:down', mouseDown) if (type === TYPES.WALL_LINE_EDIT) { addCircleByLine(currentWallLineRef.current) } }, [type]) useEffect(() => { canvas .getObjects() .filter((obj) => obj.name === 'outerLine') .forEach((line) => { line.set({ stroke: 'black' }) }) if (currentObject?.name === 'outerLine') { currentObject.set({ stroke: '#EA10AC' }) canvas.renderAll() } }, [currentObject]) const removeOuterLineEditCircle = () => { canvas.remove(...canvas.getObjects().filter((obj) => obj.name === 'outerLineEditCircleStart' || obj.name === 'outerLineEditCircleEnd')) } const mouseDown = (e) => { removeOuterLineEditCircle() if (!e.target || (e.target && e.target.name !== 'outerLine')) { currentWallLineRef.current = null return } currentWallLineRef.current = e.target if (type === TYPES.WALL_LINE_EDIT) { addCircleByLine(currentWallLineRef.current) } } const addCircleByLine = (line) => { const direction = currentWallLineRef.current?.direction let startPoint let endPoint let circle1, circle2 if (direction === 'top' || direction === 'bottom') { // y1, y2중에 더 작은 값이 startPoint, 더 큰 값이 endPoint if (line.y1 > line.y2) { startPoint = { x: line.x2, y: line.y2 } endPoint = { x: line.x1, y: line.y1 } } else { startPoint = { x: line.x1, y: line.y1 } endPoint = { x: line.x2, y: line.y2 } } arrow1Ref.current = 'down' arrow2Ref.current = 'up' } else { // x1, x2중에 더 작은 값이 startPoint, 더 큰 값이 endPoint if (line.x1 > line.x2) { startPoint = { x: line.x2, y: line.y2 } endPoint = { x: line.x1, y: line.y1 } } else { startPoint = { x: line.x1, y: line.y1 } endPoint = { x: line.x2, y: line.y2 } } arrow1Ref.current = 'right' arrow2Ref.current = 'left' } circle1 = new fabric.Circle({ radius: 10, fill: 'red', top: startPoint.y - 10, left: startPoint.x - 10, name: 'outerLineEditCircleStart', hasControls: false, hasBorders: false, lockMovementX: true, lockMovementY: true, }) circle2 = new fabric.Circle({ radius: 10, fill: 'blue', top: endPoint.y - 10, left: endPoint.x - 10, name: 'outerLineEditCircleEnd', hasControls: false, hasBorders: false, lockMovementX: true, lockMovementY: true, }) wallLineEditRef.current.setArrow() canvas.add(circle1) canvas.add(circle2) } const handleSave = () => { if (currentWallLineRef.current === null) { // alert('보조선을 먼저 선택하세요') return } switch (type) { case TYPES.WALL_LINE_EDIT: handleWallLineEditSave() break case TYPES.OFFSET: handleOffsetSave() break } } const handleWallLineEditSave = () => { const startPoint = canvas .getObjects() .filter((obj) => obj.name === 'outerLineEditCircleStart') .map((obj) => { return { x: obj.left + obj.radius, y: obj.top + obj.radius, } })[0] const endPoint = canvas .getObjects() .filter((obj) => obj.name === 'outerLineEditCircleEnd') .map((obj) => { return { x: obj.left + obj.radius, y: obj.top + obj.radius, } })[0] const length = Number(length1Ref.current.value) / 10 const length2 = Number(length2Ref.current.value) / 10 const currentIdx = currentWallLineRef.current.idx const currentDirection = currentWallLineRef.current.direction let point1, point2, point3 if (radioTypeRef.current === '1') { // 1지점 선택시 방향은 down, right만 가능 if (arrow1Ref.current === 'down') { if (currentDirection === 'top') { point1 = { x: endPoint.x, y: endPoint.y } point2 = { x: startPoint.x, y: startPoint.y + length } point3 = { x: startPoint.x, y: startPoint.y } } else { point1 = { x: startPoint.x, y: startPoint.y } point2 = { x: startPoint.x, y: startPoint.y + length } point3 = { x: endPoint.x, y: endPoint.y } } } else { if (currentDirection === 'left') { point1 = { x: endPoint.x, y: endPoint.y } point2 = { x: startPoint.x + length, y: startPoint.y } point3 = { x: startPoint.x, y: startPoint.y } } else { point1 = { x: startPoint.x, y: startPoint.y } point2 = { x: startPoint.x + length, y: startPoint.y } point3 = { x: endPoint.x, y: endPoint.y } } } } else { // 2지점일 경우 방향은 up, left만 가능 if (arrow2Ref.current === 'up') { if (currentDirection === 'bottom') { point1 = { x: startPoint.x, y: startPoint.y } point2 = { x: endPoint.x, y: endPoint.y - length2 } point3 = { x: endPoint.x, y: endPoint.y } } else { point1 = { x: endPoint.x, y: endPoint.y } point2 = { x: endPoint.x, y: endPoint.y - length2 } point3 = { x: startPoint.x, y: startPoint.y } } } else { if (currentDirection === 'right') { point1 = { x: startPoint.x, y: startPoint.y } point2 = { x: endPoint.x - length2, y: endPoint.y } point3 = { x: endPoint.x, y: endPoint.y } } else { point1 = { x: endPoint.x, y: endPoint.y } point2 = { x: endPoint.x - length2, y: endPoint.y } point3 = { x: startPoint.x, y: startPoint.y } } } } reArrangeOuterLine(currentIdx + 1) drawLine(point1, point2, currentIdx) drawLine(point2, point3, currentIdx + 1) removeOuterLineEditCircle() canvas.remove(currentWallLineRef.current) currentWallLineRef.current = null canvas.renderAll() canvas .getObjects() .filter((obj) => obj.name === 'outerLine') .forEach((obj) => obj.fire('modified')) } /** * outreLine의 index를 조절한다. * @param idxParam * @param isNegative */ const reArrangeOuterLine = (idxParam, isNegative = false) => { const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') outerLines.forEach((outerLine) => { if (outerLine.idx >= idxParam) { outerLine.idx = isNegative ? outerLine.idx - 1 : outerLine.idx + 1 } }) } /** * offset 저장 */ const handleOffsetSave = () => { if (!currentObject) return const currentLine = currentObject const currentVector = currentLine.y1 === currentLine.y2 ? 'horizontal' : 'vertical' const canDirections = currentVector === 'horizontal' ? ['up', 'down'] : ['left', 'right'] if (!canDirections.includes(arrow1Ref.current)) { alert('방향을 다시 선택하세요') return } const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') outerLines.sort((a, b) => a.idx - b.idx) const currentIdx = currentLine.idx const prevIdx = currentIdx - 1 <= 0 ? outerLines.length : currentIdx - 1 const nextIdx = currentLine.idx + 1 > outerLines.length ? 1 : currentIdx + 1 const prevLine = outerLines.find((line) => line.idx === prevIdx) const nextLine = outerLines.find((line) => line.idx === nextIdx) const prevVector = prevLine.y1 === prevLine.y2 ? 'horizontal' : 'vertical' const nextVector = nextLine.y1 === nextLine.y2 ? 'horizontal' : 'vertical' const offsetLength = Big(Number(length1Ref.current.value)).div(10) if (offsetLength.eq(0)) return const currentLineMinX = Big(Math.max(currentLine.x1, currentLine.x2)) const currentLineMaxX = Big(Math.max(currentLine.x1, currentLine.x2)) const currentLineMinY = Big(Math.max(currentLine.y1, currentLine.y2)) const currentLineMaxY = Big(Math.max(currentLine.y1, currentLine.y2)) if (currentVector === 'horizontal') { const prevX = currentLine.x1 < currentLine.x2 ? Math.min(currentLine.x1, currentLine.x2) : Math.max(currentLine.x1, currentLine.x2) const nextX = currentLine.x1 < currentLine.x2 ? Math.max(currentLine.x1, currentLine.x2) : Math.min(currentLine.x1, currentLine.x2) if (arrow1Ref.current === 'up') { currentLine.set({ y1: currentLineMaxY.minus(offsetLength).toNumber(), y2: currentLineMaxY.minus(offsetLength).toNumber() }) if (prevVector === currentVector) { const point1 = { x: prevX, y: prevLine.y2 } const point2 = { x: prevX, y: currentLine.y1 } reArrangeOuterLine(currentIdx) drawLine(point1, point2, currentIdx, arrow1Ref.current) } else { if (Big(prevLine.y1).minus(currentLineMinY).abs().lte(Big(prevLine.y2).minus(currentLineMinY).abs())) { prevLine.set({ y1: currentLine.y1 }) } else { prevLine.set({ y2: currentLine.y1 }) } if (Big(prevLine.y1).minus(Big(prevLine.y2)).eq(0)) { reArrangeOuterLine(currentIdx - 1, true) canvas.remove(prevLine) } } if (nextVector === currentVector) { const point1 = { x: nextX, y: nextLine.y2 } const point2 = { x: nextX, y: currentLine.y1 } reArrangeOuterLine(currentIdx + 1) drawLine(point1, point2, currentIdx + 1, arrow1Ref.current) } else { if (Big(nextLine.y1).minus(currentLineMaxY).abs().lte(Big(nextLine.y2).minus(currentLineMaxY).abs())) { nextLine.set({ y1: currentLine.y1 }) } else { nextLine.set({ y2: currentLine.y1 }) } if (Big(nextLine.y1).minus(Big(nextLine.y2)).eq(0)) { reArrangeOuterLine(currentIdx + 1, true) canvas.remove(nextLine) } } } if (arrow1Ref.current === 'down') { currentLine.set({ y1: currentLineMaxY.plus(offsetLength).toNumber(), y2: currentLineMaxY.plus(offsetLength).toNumber() }) if (prevVector === currentVector) { const point1 = { x: prevX, y: prevLine.y2 } const point2 = { x: prevX, y: currentLine.y1 } reArrangeOuterLine(currentIdx) drawLine(point1, point2, currentIdx, arrow1Ref.current) } else { if (Big(prevLine.y1).minus(currentLineMinY).abs().lte(Big(prevLine.y2).minus(currentLineMinY).abs())) { prevLine.set({ y1: currentLine.y1 }) } else { prevLine.set({ y2: currentLine.y1 }) } if (Big(prevLine.y1).minus(Big(prevLine.y2)).eq(0)) { reArrangeOuterLine(currentIdx - 1, true) canvas.remove(prevLine) } } if (nextVector === currentVector) { const point1 = { x: nextX, y: nextLine.y2 } const point2 = { x: nextX, y: currentLine.y1 } reArrangeOuterLine(currentIdx + 1) drawLine(point1, point2, currentIdx + 1, arrow1Ref.current) } else { if (Big(nextLine.y1).minus(currentLineMaxY).abs().lte(Big(nextLine.y2).minus(currentLineMaxY).abs())) { nextLine.set({ y1: currentLine.y1 }) } else { nextLine.set({ y2: currentLine.y1 }) } if (Big(nextLine.y1).minus(Big(nextLine.y2)).eq(0)) { reArrangeOuterLine(currentIdx + 1, true) canvas.remove(nextLine) } } } } else { const prevY = currentLine.y1 < currentLine.y2 ? Math.min(currentLine.y1, currentLine.y2) : Math.max(currentLine.y1, currentLine.y2) const nextY = currentLine.y1 < currentLine.y2 ? Math.max(currentLine.y1, currentLine.y2) : Math.min(currentLine.y1, currentLine.y2) if (arrow1Ref.current === 'left') { currentLine.set({ x1: currentLineMaxX.minus(offsetLength).toNumber(), x2: currentLineMaxX.minus(offsetLength).toNumber() }) if (prevVector === currentVector) { const point1 = { x: prevLine.x2, y: prevY } const point2 = { x: currentLine.x1, y: prevY } reArrangeOuterLine(currentIdx) drawLine(point1, point2, currentIdx, arrow1Ref.current) } else { if (Big(prevLine.x1).minus(currentLineMinX).abs().lte(Big(prevLine.x2).minus(currentLineMinX).abs())) { prevLine.set({ x1: currentLine.x1 }) } else { prevLine.set({ x2: currentLine.x1 }) } if (Big(prevLine.x1).minus(Big(prevLine.x2)).eq(0)) { reArrangeOuterLine(currentIdx - 1, true) canvas.remove(prevLine) } } if (nextVector === currentVector) { const point1 = { x: currentLine.x2, y: nextY } const point2 = { x: nextLine.x1, y: nextY } reArrangeOuterLine(currentIdx + 1) drawLine(point1, point2, currentIdx + 1, arrow1Ref.current) } else { if (Big(nextLine.x1).minus(currentLineMaxX).abs().lte(Big(nextLine.x2).minus(currentLineMaxX).abs())) { nextLine.set({ x1: currentLine.x2 }) } else { nextLine.set({ x2: currentLine.x2 }) } if (Big(nextLine.x1).minus(Big(nextLine.x2)).eq(0)) { reArrangeOuterLine(currentIdx + 1, true) canvas.remove(nextLine) } } } if (arrow1Ref.current === 'right') { currentLine.set({ x1: currentLineMaxX.plus(offsetLength).toNumber(), x2: currentLineMaxX.plus(offsetLength).toNumber() }) if (prevVector === currentVector) { const point1 = { x: prevLine.x2, y: prevY } const point2 = { x: currentLine.x1, y: prevY } reArrangeOuterLine(currentIdx) drawLine(point1, point2, currentIdx, arrow1Ref.current) } else { if (Big(prevLine.x1).minus(currentLineMinX).abs().lte(Big(prevLine.x2).minus(currentLineMinX).abs())) { prevLine.set({ x1: currentLine.x1 }) } else { prevLine.set({ x2: currentLine.x1 }) } if (Big(prevLine.x1).minus(Big(prevLine.x2)).eq(0)) { reArrangeOuterLine(currentIdx - 1, true) canvas.remove(prevLine) } } if (nextVector === currentVector) { const point1 = { x: currentLine.x2, y: nextY } const point2 = { x: nextLine.x1, y: nextY } reArrangeOuterLine(currentIdx + 1) drawLine(point1, point2, currentIdx + 1, arrow1Ref.current) } else { if (Big(nextLine.x1).minus(currentLineMaxX).abs().lte(Big(nextLine.x2).minus(currentLineMaxX).abs())) { nextLine.set({ x1: currentLine.x2 }) } else { nextLine.set({ x2: currentLine.x2 }) } if (Big(nextLine.x1).minus(Big(nextLine.x2)).eq(0)) { reArrangeOuterLine(currentIdx + 1, true) canvas.remove(nextLine) } } } } const newOuterLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') newOuterLines.sort((a, b) => a.idx - b.idx) newOuterLines.forEach((line, idx) => { line.fire('modified') }) canvas.renderAll() } return { type, setType, buttonMenu, TYPES, length1Ref, length2Ref, radioTypeRef, currentWallLineRef, arrow1Ref, arrow2Ref, handleSave, wallLineEditRef, } }