diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index ab445ca9..f286679d 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -195,7 +195,12 @@ export default function CanvasMenu(props) {
- + {canvasZoom}%
diff --git a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx index ebdc6bb9..828b2ad3 100644 --- a/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx +++ b/src/components/floor-plan/modal/roofShape/RoofShapePassivitySetting.jsx @@ -4,15 +4,26 @@ import Eaves from '@/components/floor-plan/modal/roofShape/passivity/Eaves' import Gable from '@/components/floor-plan/modal/roofShape/passivity/Gable' import Shed from '@/components/floor-plan/modal/roofShape/passivity/Shed' import { useMessage } from '@/hooks/useMessage' +import { useRoofShapePassivitySetting } from '@/hooks/roofcover/useRoofShapePassivitySetting' export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySettingModal }) { + const { handleSave, handleConfirm, handleRollback, buttons, type, setType, TYPES, offsetRef, pitchRef } = useRoofShapePassivitySetting() const { getMessage } = useMessage() - const [buttonAct, setButtonAct] = useState(1) - const buttons = [ - { id: 1, name: getMessage('eaves') }, - { id: 2, name: getMessage('gable') }, - { id: 3, name: getMessage('windage') }, - ] + + const eavesProps = { + offsetRef, + pitchRef, + } + + const gableProps = { + offsetRef, + pitchRef, + } + + const shedProps = { + offsetRef, + } + return (
@@ -25,7 +36,7 @@ export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySet
{buttons.map((button) => ( - ))} @@ -33,17 +44,23 @@ export default function RoofShapePassivitySetting({ setShowRoofShapePassivitySet
{getMessage('setting')}
- {buttonAct === 1 && } - {buttonAct === 2 && } - {buttonAct === 3 && } + {type === TYPES.EAVES && } + {type === TYPES.GABLE && } + {type === TYPES.SHED && }
- - + +
- +
diff --git a/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx b/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx index 22e15a34..3a16b5c0 100644 --- a/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx +++ b/src/components/floor-plan/modal/roofShape/passivity/Eaves.jsx @@ -1,6 +1,6 @@ import { useMessage } from '@/hooks/useMessage' -export default function Eaves() { +export default function Eaves({ offsetRef, pitchRef }) { const { getMessage } = useMessage() return ( <> @@ -9,7 +9,7 @@ export default function Eaves() { {getMessage('slope')}
- +
@@ -18,7 +18,7 @@ export default function Eaves() { {getMessage('eaves.offset')}
- +
mm diff --git a/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx b/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx index e2eb6342..69b2cf9d 100644 --- a/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx +++ b/src/components/floor-plan/modal/roofShape/passivity/Gable.jsx @@ -1,6 +1,6 @@ import { useMessage } from '@/hooks/useMessage' -export default function Gable() { +export default function Gable({ offsetRef, pitchRef }) { const { getMessage } = useMessage() return ( <> @@ -9,7 +9,7 @@ export default function Gable() { {getMessage('slope')}
- +
@@ -18,7 +18,7 @@ export default function Gable() { {getMessage('gable.offset')}
- +
mm diff --git a/src/components/floor-plan/modal/roofShape/passivity/Shed.js b/src/components/floor-plan/modal/roofShape/passivity/Shed.js index dfdee92c..474c2f60 100644 --- a/src/components/floor-plan/modal/roofShape/passivity/Shed.js +++ b/src/components/floor-plan/modal/roofShape/passivity/Shed.js @@ -1,6 +1,6 @@ import { useMessage } from '@/hooks/useMessage' -export default function Shed() { +export default function Shed({ offsetRef }) { const { getMessage } = useMessage() return ( <> @@ -9,7 +9,7 @@ export default function Shed() { {getMessage('shed.width')}
- +
mm diff --git a/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx b/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx index fcd48af1..a013934c 100644 --- a/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx +++ b/src/components/floor-plan/modal/wallLineOffset/WallLineOffsetSetting.jsx @@ -7,9 +7,20 @@ import { useWallLineOffsetSetting } from '@/hooks/roofcover/useWallLineOffsetSet export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModal }) { const { getMessage } = useMessage() - const { type, setType, buttonMenu, currentWallLineRef, TYPES, radioTypeRef, arrow1Ref, arrow2Ref, length1Ref, length2Ref, handleSave } = - useWallLineOffsetSetting() - const [buttonAct, setButtonAct] = useState(1) + const { + type, + setType, + buttonMenu, + currentWallLineRef, + TYPES, + radioTypeRef, + arrow1Ref, + arrow2Ref, + length1Ref, + length2Ref, + handleSave, + wallLineEditRef, + } = useWallLineOffsetSetting() const wallLineProps = { length1Ref, @@ -45,7 +56,7 @@ export default function WallLineOffsetSetting({ setShowWallLineOffsetSettingModa
{getMessage('setting')}
- {type === TYPES.WALL_LINE_EDIT && } + {type === TYPES.WALL_LINE_EDIT && } {type === TYPES.OFFSET && }
diff --git a/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx b/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx index 4f5c6ea7..15ccefa3 100644 --- a/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx +++ b/src/components/floor-plan/modal/wallLineOffset/type/WallLine.jsx @@ -1,8 +1,8 @@ import { useMessage } from '@/hooks/useMessage' -import { useEffect, useState } from 'react' +import { forwardRef, useEffect, useImperativeHandle, useState } from 'react' import { useEvent } from '@/hooks/useEvent' -export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }) { +export default forwardRef(function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, radioTypeRef, currentWallLineRef }, ref) { const { getMessage } = useMessage() const { addDocumentEventListener, initEvent } = useEvent() const [type, setType] = useState(1) @@ -10,76 +10,25 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref, const [arrow2, setArrow2] = useState('up') useEffect(() => { - addDocumentEventListener('keydown', document, keyDown) - return () => { initEvent() } }, []) + useImperativeHandle(ref, () => ({ + setArrow, + })) + + const setArrow = () => { + setArrow1(arrow1Ref.current) + setArrow2(arrow2Ref.current) + } + const onChange = (e) => { setType(Number(e.target.value)) radioTypeRef.current = e.target.value } - const handleBtnClick = (direction) => { - document.dispatchEvent(new KeyboardEvent('keydown', { key: direction })) - } - - const keyDown = (e) => { - if (currentWallLineRef.current === null) { - alert('보조선을 먼저 선택하세요') - return - } - - const key = e.key - - switch (key) { - case 'Down': // IE/Edge에서 사용되는 값 - case 'ArrowDown': { - if (radioTypeRef.current === '1') { - setArrow1('down') - arrow1Ref.current = 'down' - } else { - setArrow2('down') - arrow2Ref.current = 'down' - } - - break - } - case 'Up': // IE/Edge에서 사용되는 값 - case 'ArrowUp': - if (radioTypeRef.current === '1') { - setArrow1('up') - arrow1Ref.current = 'up' - } else { - setArrow2('up') - arrow2Ref.current = 'up' - } - break - case 'Left': // IE/Edge에서 사용되는 값 - case 'ArrowLeft': - if (radioTypeRef.current === '1') { - setArrow1('left') - arrow1Ref.current = 'left' - } else { - setArrow2('left') - arrow2Ref.current = 'left' - } - break - case 'Right': // IE/Edge에서 사용되는 값 - case 'ArrowRight': - if (radioTypeRef.current === '1') { - setArrow1('right') - arrow1Ref.current = 'right' - } else { - setArrow2('right') - arrow2Ref.current = 'right' - } - break - } - } - return ( <>
@@ -107,10 +56,10 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
- - - - + + + +
@@ -141,10 +90,10 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
- - - - + + + +
@@ -155,4 +104,4 @@ export default function WallLine({ length1Ref, length2Ref, arrow1Ref, arrow2Ref,
) -} +}) diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index de213685..a2810163 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -27,27 +27,16 @@ import { calculateDistance, calculateIntersection, distanceBetweenPoints, findCl import { fabric } from 'fabric' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' +// 보조선 작성 export function useAuxiliaryDrawing() { const canvas = useRecoilValue(canvasState) - const { - addCanvasMouseEventListener, - addDocumentEventListener, - removeAllMouseEventListeners, - removeAllDocumentEventListeners, - removeMouseEvent, - removeMouseLine, - initEvent, - } = useEvent() + const { addCanvasMouseEventListener, addDocumentEventListener, removeMouseLine, initEvent } = useEvent() const { getIntersectMousePoint } = useMouse() const { addLine, removeLine } = useLine() const { tempGridMode } = useTempGrid() const { getAdsorptionPoints } = useAdsorptionPoint() - const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) - const adsorptionPointAddMode = useRecoilValue(adsorptionPointAddModeState) - const adsorptionPointMode = useRecoilValue(adsorptionPointModeState) const adsorptionRange = useRecoilValue(adsorptionRangeState) - const interval = useRecoilValue(dotLineIntervalSelector) // 가로 세로 간격 const [buttonAct, setButtonAct] = useState(1) diff --git a/src/hooks/roofcover/useEavesGableEdit.js b/src/hooks/roofcover/useEavesGableEdit.js index d2c69303..96ec864f 100644 --- a/src/hooks/roofcover/useEavesGableEdit.js +++ b/src/hooks/roofcover/useEavesGableEdit.js @@ -7,6 +7,7 @@ import { LINE_TYPE } from '@/common/common' import { useLine } from '@/hooks/useLine' import { useMode } from '@/hooks/useMode' +// 처마.케라바 변경 export function useEavesGableEdit() { const canvas = useRecoilValue(canvasState) const { getMessage } = useMessage() diff --git a/src/hooks/roofcover/useOuterLineWall.js b/src/hooks/roofcover/useOuterLineWall.js index e37495d9..d3b54c1c 100644 --- a/src/hooks/roofcover/useOuterLineWall.js +++ b/src/hooks/roofcover/useOuterLineWall.js @@ -30,6 +30,7 @@ import { import { calculateAngle } from '@/util/qpolygon-utils' import { fabric } from 'fabric' +//외벽선 그리기 export function useOuterLineWall() { const canvas = useRecoilValue(canvasState) const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = diff --git a/src/hooks/roofcover/usePropertiesSetting.js b/src/hooks/roofcover/usePropertiesSetting.js index ebd5182e..a2905f43 100644 --- a/src/hooks/roofcover/usePropertiesSetting.js +++ b/src/hooks/roofcover/usePropertiesSetting.js @@ -7,6 +7,7 @@ import { usePolygon } from '@/hooks/usePolygon' import { useLine } from '@/hooks/useLine' import { outerLinePointsState } from '@/store/outerLineAtom' +// 외벽선 속성 설정 export function usePropertiesSetting() { const canvas = useRecoilValue(canvasState) @@ -19,17 +20,7 @@ export function usePropertiesSetting() { const { removeLine, hideLine } = useLine() useEffect(() => { - if (!currentObject) { - return - } - if (currentObject.name !== 'outerLine') { - return - } - - const type = currentObject.attributes?.type - const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') - lines.forEach((line) => { const lineType = line.attributes?.type if (!lineType) { @@ -39,6 +30,14 @@ export function usePropertiesSetting() { }) } }) + if (!currentObject) { + return + } + if (currentObject.name !== 'outerLine') { + return + } + + const type = currentObject.attributes?.type if (!type) { currentObject.set({ @@ -46,6 +45,8 @@ export function usePropertiesSetting() { strokeWidth: 4, }) } + + canvas.renderAll() }, [currentObject]) const history = useRef([]) @@ -106,6 +107,7 @@ export function usePropertiesSetting() { } const lastLine = history.current.pop() + delete lastLine.attributes lastLine.set({ stroke: '#000000', strokeWidth: 4, @@ -131,7 +133,7 @@ export function usePropertiesSetting() { hideLine(line) }) - const wall = addPolygonByLines(lines, { name: 'WallLine', fill: 'transparent', stroke: 'black' }) + const wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' }) wall.lines = [...lines] diff --git a/src/hooks/roofcover/useRoofShapePassivitySetting.js b/src/hooks/roofcover/useRoofShapePassivitySetting.js new file mode 100644 index 00000000..245ee0d9 --- /dev/null +++ b/src/hooks/roofcover/useRoofShapePassivitySetting.js @@ -0,0 +1,175 @@ +import { canvasState, currentObjectState } from '@/store/canvasAtom' +import { useRecoilValue } from 'recoil' +import { useEffect, useRef, useState } from 'react' +import { useLine } from '@/hooks/useLine' +import { useMessage } from '@/hooks/useMessage' +import { useEvent } from '@/hooks/useEvent' +import { LINE_TYPE } from '@/common/common' +import { useMode } from '@/hooks/useMode' +import { usePolygon } from '@/hooks/usePolygon' + +//지붕형상 수동 설정 +export function useRoofShapePassivitySetting() { + const TYPES = { + EAVES: 'eaves', + GABLE: 'gable', + SHED: 'shed', + } + const canvas = useRecoilValue(canvasState) + const { getMessage } = useMessage() + const { showLine, hideLine } = useLine() + const { addCanvasMouseEventListener, initEvent } = useEvent() + const { drawRoofPolygon } = useMode() + const { addPolygonByLines } = usePolygon() + const currentObject = useRecoilValue(currentObjectState) + const offsetRef = useRef(null) + const pitchRef = useRef(null) + const currentLineRef = useRef(null) + + const history = useRef([]) + + const [type, setType] = useState(TYPES.EAVES) + + const buttons = [ + { id: 1, name: getMessage('eaves'), type: TYPES.EAVES }, + { id: 2, name: getMessage('gable'), type: TYPES.GABLE }, + { id: 3, name: getMessage('windage'), type: TYPES.SHED }, + ] + + useEffect(() => { + addCanvasMouseEventListener('mouse:down', mouseDown) + const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') + canvas.remove(wallLines) + + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + outerLines.forEach((outerLine, idx) => { + if (idx === 0) { + currentLineRef.current = outerLine + } + outerLine.set({ selectable: true }) + showLine(outerLine) + outerLine.bringToFront() + }) + canvas?.renderAll() + + return () => { + canvas?.discardActiveObject() + initEvent() + } + }, []) + + useEffect(() => { + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + lines.forEach((line) => { + line.set({ + stroke: '#000000', + strokeWidth: 4, + }) + }) + if (!currentObject) { + return + } + if (currentObject.name !== 'outerLine') { + return + } + + currentObject.set({ + stroke: '#EA10AC', + strokeWidth: 4, + }) + + currentLineRef.current = currentObject + + canvas.renderAll() + }, [currentObject]) + + const mouseDown = (e) => { + if (!e.target) { + currentLineRef.current = null + return + } + + if (e.target && e.target.name === 'outerLine') { + currentLineRef.current = e.target + } + } + + const nextLineFocus = (selectedLine) => { + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + const index = lines.findIndex((line) => line === selectedLine) + + const nextLine = lines[index + 1] || lines[0] + canvas.setActiveObject(nextLine) + } + + const handleConfirm = () => { + if (!currentLineRef.current) { + alert('선택된 외곽선이 없습니다.') + return + } + let attributes + const offset = Number(offsetRef.current.value) / 10 + if (type === TYPES.EAVES) { + attributes = { + type: LINE_TYPE.WALLLINE.EAVES, + offset, + pitch: pitchRef.current.value, + } + } else if (type === TYPES.GABLE) { + attributes = { + type: LINE_TYPE.WALLLINE.GABLE, + pitch: pitchRef.current.value, + offset, + } + } else if (type === TYPES.SHED) { + attributes = { + type: LINE_TYPE.WALLLINE.SHED, + offset, + } + } + + currentLineRef.current.set({ + attributes, + }) + + history.current.push(currentLineRef.current) + nextLineFocus(currentLineRef.current) + } + + const handleRollback = () => { + if (history.current.length === 0) { + return + } + const lastLine = history.current.pop() + + delete lastLine.attributes + lastLine.set({ + stroke: '#000000', + strokeWidth: 4, + }) + + canvas.setActiveObject(lastLine) + canvas.renderAll() + } + + const handleSave = (fn) => { + const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine') + const lines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + exceptObjs.forEach((obj) => { + canvas.remove(obj) + }) + + lines.forEach((line) => { + hideLine(line) + }) + + const wall = addPolygonByLines(lines, { name: 'wallLine', fill: 'transparent', stroke: 'black' }) + + wall.lines = [...lines] + + const roof = drawRoofPolygon(wall) + canvas.renderAll() + fn(false) + } + return { handleSave, handleConfirm, buttons, type, setType, TYPES, offsetRef, pitchRef, handleRollback } +} diff --git a/src/hooks/roofcover/useRoofShapeSetting.js b/src/hooks/roofcover/useRoofShapeSetting.js index 2c51b2bd..c0d65a21 100644 --- a/src/hooks/roofcover/useRoofShapeSetting.js +++ b/src/hooks/roofcover/useRoofShapeSetting.js @@ -7,6 +7,7 @@ import { usePolygon } from '@/hooks/usePolygon' import { useMode } from '@/hooks/useMode' import { useLine } from '@/hooks/useLine' +// 지붕형상 설정 export function useRoofShapeSetting() { const [shapeNum, setShapeNum] = useState(1) const [buttonAct, setButtonAct] = useState(1) diff --git a/src/hooks/roofcover/useWallLineOffsetSetting.js b/src/hooks/roofcover/useWallLineOffsetSetting.js index 6536bc80..882635e0 100644 --- a/src/hooks/roofcover/useWallLineOffsetSetting.js +++ b/src/hooks/roofcover/useWallLineOffsetSetting.js @@ -5,11 +5,13 @@ import { useMessage } from '@/hooks/useMessage' import { useEvent } from '@/hooks/useEvent' import { useLine } from '@/hooks/useLine' +// 외벽선 편집 및 오프셋 export function useWallLineOffsetSetting() { const canvas = useRecoilValue(canvasState) const { showLine, addLine } = useLine() const { getMessage } = useMessage() const { addCanvasMouseEventListener, initEvent } = useEvent() + const wallLineEditRef = useRef(null) const length1Ref = useRef(null) const length2Ref = useRef(null) const radioTypeRef = useRef('1') @@ -17,6 +19,23 @@ export function useWallLineOffsetSetting() { const arrow1Ref = useRef(null) const arrow2Ref = useRef(null) + const drawLine = (point1, point2, idx) => { + 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: currentWallLineRef.current.direction, + }) + + line.attributes = { ...currentWallLineRef.current.attributes } + } + const TYPES = { WALL_LINE_EDIT: 'wallLineEdit', OFFSET: 'offset', @@ -36,37 +55,114 @@ export function useWallLineOffsetSetting() { showLine(outerLine) }) - const roofs = canvas.getObjects().filter((obj) => obj.name === 'roofBase') - - roofs.forEach((roof) => { - roof.innerLines.forEach((innerLine) => { - canvas.remove(innerLine) - }) - canvas.remove(roof) + //outerLine과 그 text만 남겨둔다. + const exceptObjs = canvas.getObjects().filter((obj) => obj.name !== 'outerLine' && obj.parent?.name !== 'outerLine') + exceptObjs.forEach((obj) => { + canvas.remove(obj) }) - const wallLines = canvas.getObjects().filter((obj) => obj.name === 'wallLine') - wallLines.forEach((wallLine) => { - canvas.remove(wallLine) - }) + canvas.setActiveObject(outerLines[0]) + currentWallLineRef.current = outerLines[0] + addCircleByLine(currentWallLineRef.current) addCanvasMouseEventListener('mouse:down', mouseDown) return () => { + removeOuterLineEditCircle() canvas.discardActiveObject() initEvent() } }, []) + useEffect(() => { + removeOuterLineEditCircle() + addCanvasMouseEventListener('mouse:down', mouseDown) + if (type === TYPES.WALL_LINE_EDIT) { + addCircleByLine(currentWallLineRef.current) + } + }, [type]) + + 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() @@ -78,19 +174,65 @@ export function useWallLineOffsetSetting() { } const handleWallLineEditSave = () => { - const direction = currentWallLineRef.current.direction - let canDirections = direction === 'left' || direction === 'right' ? ['up', 'down'] : ['left', 'right'] - if (radioTypeRef === 1) { - if (!canDirections.includes(arrow1Ref.current)) { - alert('방향을 다시 선택하세요') - return + 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 currentIdx = currentWallLineRef.current.idx + let point1, point2, point3 + if (radioTypeRef.current === '1') { + // 1지점 선택시 방향은 down, right만 가능 + if (arrow1Ref.current === 'down') { + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: startPoint.x, y: startPoint.y + length } + point3 = { x: endPoint.x, y: endPoint.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 { - if (!canDirections.includes(arrow2Ref.current)) { - alert('방향을 다시 선택하세요') - return + // 2지점일 경우 방향은 up, left만 가능 + if (arrow2Ref.current === 'up') { + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: startPoint.x, y: startPoint.y - length } + point3 = { x: endPoint.x, y: endPoint.y } + } else { + point1 = { x: startPoint.x, y: startPoint.y } + point2 = { x: startPoint.x - length, y: startPoint.y } + point3 = { x: endPoint.x, y: endPoint.y } } } + + const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + outerLines.forEach((outerLine) => { + if (outerLine.idx >= currentIdx + 1) { + outerLine.idx = outerLine.idx + 1 + } + }) + + drawLine(point1, point2, currentIdx) + drawLine(point2, point3, currentIdx + 1) + + removeOuterLineEditCircle() + canvas.remove(currentWallLineRef.current) + currentWallLineRef.current = null + canvas.renderAll() } const handleOffsetSave = () => { @@ -103,26 +245,100 @@ export function useWallLineOffsetSetting() { } const outerLines = canvas.getObjects().filter((obj) => obj.name === 'outerLine') + const idx = currentWallLineRef.current.idx - const prevLine = outerLines.find((line) => (line.idx === idx - 1 < 0 ? outerLines.length - 1 : idx - 1)) + const prevIdx = idx - 1 <= 0 ? outerLines.length : idx - 1 + const nextIdx = idx + 1 > outerLines.length ? 1 : idx + 1 + const currentLine = currentWallLineRef.current - const nextLine = outerLines.find((line) => (line.idx === idx + 1 > outerLines.length - 1 ? 0 : idx + 1)) + const prevLine = outerLines.find((line) => line.idx === prevIdx) + const nextLine = outerLines.find((line) => line.idx === nextIdx) + + const length = length1Ref.current.value / 10 + switch (arrow1Ref.current) { case 'up': { - console.log(prevLine, currentLine, nextLine) + const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2)) + currentLine.set({ y1: currentLineY - length, y2: currentLineY - length }) + + if (Math.abs(currentLineY - prevLine.y1) < 2) { + prevLine.set({ y1: currentLineY - length }) + } else { + prevLine.set({ y2: currentLineY - length }) + } + if (Math.abs(currentLineY - nextLine.y1) < 2) { + nextLine.set({ y1: currentLineY - length }) + } else { + nextLine.set({ y2: currentLineY - length }) + } + break } case 'down': { + const currentLineY = Math.floor(Math.max(currentLine.y1, currentLine.y2)) + currentLine.set({ y1: currentLineY + length, y2: currentLineY + length }) + + if (Math.abs(currentLineY - prevLine.y1) < 2) { + prevLine.set({ y1: currentLineY + length }) + } else { + prevLine.set({ y2: currentLineY + length }) + } + if (Math.abs(currentLineY - nextLine.y1) < 2) { + nextLine.set({ y1: currentLineY + length }) + } else { + nextLine.set({ y2: currentLineY + length }) + } break } case 'left': { + const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2)) + currentLine.set({ x1: currentLineX - length, x2: currentLineX - length }) + + if (Math.abs(currentLineX - prevLine.x1) < 2) { + prevLine.set({ x1: currentLineX - length }) + } else { + prevLine.set({ x2: currentLineX - length }) + } + if (Math.abs(currentLineX - nextLine.x1) < 2) { + nextLine.set({ x1: currentLineX - length }) + } else { + nextLine.set({ x2: currentLineX - length }) + } break } case 'right': { + const currentLineX = Math.floor(Math.max(currentLine.x1, currentLine.x2)) + currentLine.set({ x1: currentLineX + length, x2: currentLineX + length }) + + if (Math.abs(currentLineX - prevLine.x1) < 2) { + prevLine.set({ x1: currentLineX + length }) + } else { + prevLine.set({ x2: currentLineX + length }) + } + if (Math.abs(currentLineX - nextLine.x1) < 2) { + nextLine.set({ x1: currentLineX + length }) + } else { + nextLine.set({ x2: currentLineX + length }) + } + break } } + canvas.renderAll() } - return { type, setType, buttonMenu, TYPES, length1Ref, length2Ref, radioTypeRef, currentWallLineRef, arrow1Ref, arrow2Ref, handleSave } + return { + type, + setType, + buttonMenu, + TYPES, + length1Ref, + length2Ref, + radioTypeRef, + currentWallLineRef, + arrow1Ref, + arrow2Ref, + handleSave, + wallLineEditRef, + } }