diff --git a/src/components/floor-plan/modal/auxiliary/AuxiliaryCopy.jsx b/src/components/floor-plan/modal/auxiliary/AuxiliaryCopy.jsx deleted file mode 100644 index 32ffc78d..00000000 --- a/src/components/floor-plan/modal/auxiliary/AuxiliaryCopy.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import { useMessage } from '@/hooks/useMessage' -import WithDraggable from '@/components/common/draggable/WithDraggable' -import { usePopup } from '@/hooks/usePopup' -import { useRecoilValue } from 'recoil' -import { contextPopupPositionState } from '@/store/popupAtom' -import { useState } from 'react' - -export default function AuxiliaryCopy(props) { - const contextPopupPosition = useRecoilValue(contextPopupPositionState) - const { id, pos = contextPopupPosition } = props - const { getMessage } = useMessage() - const { closePopup } = usePopup() - const [arrow1, setArrow1] = useState(null) - const [arrow2, setArrow2] = useState(null) - return ( - -
-
-

{getMessage('modal.auxiliary.copy')}

- -
-
-
{getMessage('modal.auxiliary.copy.info')}
-
-
-
-

{getMessage('length')}

-
-
- -
- mm -
- - -
-
-
-
- -
- mm -
- - -
-
-
-
-
-
- -
-
-
-
- ) -} diff --git a/src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx b/src/components/floor-plan/modal/auxiliary/AuxiliaryEdit.jsx similarity index 66% rename from src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx rename to src/components/floor-plan/modal/auxiliary/AuxiliaryEdit.jsx index 733173b4..da00ac06 100644 --- a/src/components/floor-plan/modal/auxiliary/AuxiliaryMove.jsx +++ b/src/components/floor-plan/modal/auxiliary/AuxiliaryEdit.jsx @@ -1,37 +1,62 @@ -'use client' - import { useMessage } from '@/hooks/useMessage' import WithDraggable from '@/components/common/draggable/WithDraggable' +import { usePopup } from '@/hooks/usePopup' import { useRecoilValue } from 'recoil' import { contextPopupPositionState } from '@/store/popupAtom' -import { usePopup } from '@/hooks/usePopup' import { useState } from 'react' +import { currentObjectState } from '@/store/canvasAtom' +import { useLine } from '@/hooks/useLine' +import { useAuxiliaryDrawing } from '@/hooks/roofcover/useAuxiliaryDrawing' -export default function AuxiliaryMove(props) { +export default function AuxiliaryEdit(props) { const contextPopupPosition = useRecoilValue(contextPopupPositionState) - const { id, pos = contextPopupPosition } = props + const { id, pos = contextPopupPosition, type } = props const { getMessage } = useMessage() const { closePopup } = usePopup() + const { move, copy } = useAuxiliaryDrawing() + const [verticalSize, setVerticalSize] = useState('0') + const [horizonSize, setHorizonSize] = useState('0') const [arrow1, setArrow1] = useState(null) const [arrow2, setArrow2] = useState(null) + const { addLine, removeLine } = useLine() + const currentObject = useRecoilValue(currentObjectState) + const handleSave = () => { + if (type === 'copy') { + if (currentObject) { + copy( + currentObject, + arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize), + arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize), + ) + } + } else { + move( + currentObject, + arrow2 === '←' ? Number(horizonSize) * -1 : Number(horizonSize), + arrow1 === '↑' ? Number(verticalSize) * -1 : Number(verticalSize), + ) + } + + closePopup(id) + } return (
-

{getMessage('modal.auxiliary.move')}

+

{getMessage(type === 'copy' ? 'modal.auxiliary.copy' : 'modal.auxiliary.move')}

-
{getMessage('modal.auxiliary.move.info')}
+
{getMessage(type === 'copy' ? 'modal.auxiliary.copy.info' : 'modal.auxiliary.move.info')}

{getMessage('length')}

- + setVerticalSize(e.target.value)} />
mm
@@ -53,7 +78,7 @@ export default function AuxiliaryMove(props) {
- + setHorizonSize(e.target.value)} />
mm
@@ -77,7 +102,9 @@ export default function AuxiliaryMove(props) {
- +
diff --git a/src/hooks/roofcover/useAuxiliaryDrawing.js b/src/hooks/roofcover/useAuxiliaryDrawing.js index 1a0d87bd..30e3982e 100644 --- a/src/hooks/roofcover/useAuxiliaryDrawing.js +++ b/src/hooks/roofcover/useAuxiliaryDrawing.js @@ -15,14 +15,12 @@ import { outerLineLength2State, outerLineTypeState, } from '@/store/outerLineAtom' -import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine, polygonToTurfPolygon } from '@/util/canvas-util' +import { calculateIntersection, distanceBetweenPoints, findClosestPoint, isPointOnLine } from '@/util/canvas-util' import { fabric } from 'fabric' import { useAdsorptionPoint } from '@/hooks/useAdsorptionPoint' import { useSwal } from '@/hooks/useSwal' -import { booleanPointInPolygon } from '@turf/turf' import { usePopup } from '@/hooks/usePopup' import { calculateAngle, isSamePoint } from '@/util/qpolygon-utils' -import { QPolygon } from '@/components/fabric/QPolygon' import { POLYGON_TYPE } from '@/common/common' // 보조선 작성 @@ -122,6 +120,38 @@ export function useAuxiliaryDrawing(id) { setOuterLineDiagonalLength(0) } + const move = (object, x, y) => { + const line = copy(object, x, y) + canvas.remove(object) + canvas.setActiveObject(line) + } + + const copy = (object, x, y) => { + return addLine([object.x1 + x, object.y1 + y, object.x2 + x, object.y2 + y], { + stroke: 'red', + strokeWidth: 1, + selectable: true, + name: 'auxiliaryLine', + }) + } + + const addBisectorLine = (target) => { + const slope = (target.y2 - target.y1) / (target.x2 - target.x1) + const bisectorSlope = -1 / slope + const length = target.length + const dx = length / Math.sqrt(1 + bisectorSlope * bisectorSlope) + const dy = bisectorSlope * dx + const endX = (target.x1 + target.x2) / 2 + const endY = (target.y1 + target.y2) / 2 + + addLine([dx, dy, endX, endY], { + stroke: 'red', + strokeWidth: 1, + selectable: true, + name: 'auxiliaryLine', + }) + } + const keydown = { outerLine: (e) => { if (mousePointerArr.current.length === 0) { @@ -130,7 +160,7 @@ export function useAuxiliaryDrawing(id) { // 포커스가 length1에 있지 않으면 length1에 포커스를 줌 const activeElem = document.activeElement if (activeElem !== length1Ref.current) { - length1Ref.current.focus() + length1Ref?.current?.focus() } const key = e.key @@ -180,7 +210,7 @@ export function useAuxiliaryDrawing(id) { const activeElem = document.activeElement if (activeElem !== length1Ref.current && activeElem !== length2Ref.current) { - length1Ref.current.focus() + length1Ref?.current?.focus() } switch (key) { @@ -871,5 +901,8 @@ export function useAuxiliaryDrawing(id) { handleRollback, buttonAct, setButtonAct, + move, + copy, + addBisectorLine, } } diff --git a/src/hooks/useContextMenu.js b/src/hooks/useContextMenu.js index 8e7028d4..1f5f59cb 100644 --- a/src/hooks/useContextMenu.js +++ b/src/hooks/useContextMenu.js @@ -1,8 +1,7 @@ import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' -import { currentMenuState, currentObjectState } from '@/store/canvasAtom' +import { canvasState, currentMenuState, currentObjectState } from '@/store/canvasAtom' import { useEffect, useState } from 'react' import { MENU } from '@/common/common' -import AuxiliaryMove from '@/components/floor-plan/modal/auxiliary/AuxiliaryMove' import AuxiliarySize from '@/components/floor-plan/modal/auxiliary/AuxiliarySize' import { usePopup } from '@/hooks/usePopup' import { v4 as uuidv4 } from 'uuid' @@ -11,7 +10,7 @@ import GridCopy from '@/components/floor-plan/modal/grid/GridCopy' import ColorPickerModal from '@/components/common/color-picker/ColorPickerModal' import { gridColorState } from '@/store/gridAtom' import { contextPopupPositionState, contextPopupState } from '@/store/popupAtom' -import AuxiliaryCopy from '@/components/floor-plan/modal/auxiliary/AuxiliaryCopy' +import AuxiliaryEdit from '@/components/floor-plan/modal/auxiliary/AuxiliaryEdit' import SizeSetting from '@/components/floor-plan/modal/object/SizeSetting' import RoofMaterialSetting from '@/components/floor-plan/modal/object/RoofMaterialSetting' import DormerOffset from '@/components/floor-plan/modal/object/DormerOffset' @@ -34,8 +33,10 @@ import CircuitNumberEdit from '@/components/floor-plan/modal/module/CircuitNumbe import { useObjectBatch } from '@/hooks/object/useObjectBatch' import { useSurfaceShapeBatch } from '@/hooks/surface/useSurfaceShapeBatch' import { fontSelector, globalFontAtom } from '@/store/fontAtom' +import { useLine } from '@/hooks/useLine' export function useContextMenu() { + const canvas = useRecoilValue(canvasState) const currentMenu = useRecoilValue(currentMenuState) // 현재 메뉴 const setContextPopupPosition = useSetRecoilState(contextPopupPositionState) // 현재 메뉴 const [contextMenu, setContextMenu] = useRecoilState(contextMenuListState) // 메뉴.object 별 context menu @@ -53,6 +54,7 @@ export function useContextMenu() { const { moveObjectBatch } = useObjectBatch({}) const { moveSurfaceShapeBatch } = useSurfaceShapeBatch() const [globalFont, setGlobalFont] = useRecoilState(globalFontAtom) + const { addLine, removeLine } = useLine() const commonTextFont = useRecoilValue(fontSelector('commonText')) const currentMenuSetting = () => { @@ -130,22 +132,57 @@ export function useContextMenu() { id: 'auxiliaryMove', name: `${getMessage('contextmenu.auxiliary.move')}(M)`, shortcut: ['m', 'M'], - component: , + component: , }, { id: 'auxiliaryCopy', name: `${getMessage('contextmenu.auxiliary.copy')}(C)`, shortcut: ['c', 'C'], - component: , + component: , }, { id: 'auxiliaryRemove', shortcut: ['d', 'D'], name: `${getMessage('contextmenu.auxiliary.remove')}(D)`, + fn: () => { + canvas.remove(currentObject) + canvas.discardActiveObject() + }, }, { id: 'auxiliaryVerticalBisector', name: getMessage('contextmenu.auxiliary.vertical.bisector'), + fn: () => { + const slope = (currentObject.y2 - currentObject.y1) / (currentObject.x2 - currentObject.x1) + const length = currentObject.length + + let startX, startY, endX, endY + if (slope === 0) { + startX = endX = (currentObject.x1 + currentObject.x2) / 2 + startY = currentObject.y2 - length / 2 + endY = currentObject.y2 + length / 2 + } else if (slope === Infinity) { + startX = currentObject.x1 - length / 2 + startY = endY = (currentObject.y1 + currentObject.y2) / 2 + endX = currentObject.x1 + length / 2 + } else { + const bisectorSlope = -1 / slope + const dx = length / 2 / Math.sqrt(1 + bisectorSlope * bisectorSlope) + const dy = bisectorSlope * dx + + startX = (currentObject.x1 + currentObject.x2) / 2 + dx + startY = (currentObject.y1 + currentObject.y2) / 2 + dy + endX = (currentObject.x1 + currentObject.x2) / 2 - dx + endY = (currentObject.y1 + currentObject.y2) / 2 - dy + } + + addLine([startX, startY, endX, endY], { + stroke: 'red', + strokeWidth: 1, + selectable: true, + name: 'auxiliaryLine', + }) + }, }, { id: 'auxiliaryCut', @@ -472,6 +509,7 @@ export function useContextMenu() { { id: 'removeAll', name: getMessage('contextmenu.remove.all'), + fn: () => {}, }, ], ])