From ad1898c289af843e092efaef315a3d00eeb68080 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Mon, 9 Sep 2024 11:07:09 +0900 Subject: [PATCH 1/6] =?UTF-8?q?context=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/context-menu/QContextMenu.jsx | 2 +- src/hooks/useMode.js | 41 ++++++++++++++++--- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/src/components/common/context-menu/QContextMenu.jsx b/src/components/common/context-menu/QContextMenu.jsx index 4658ef6f..cacb041c 100644 --- a/src/components/common/context-menu/QContextMenu.jsx +++ b/src/components/common/context-menu/QContextMenu.jsx @@ -12,7 +12,7 @@ export default function QContextMenu(props) { let contextType = '' if (activeObject) { - if (activeObject.initOptions) { + if (activeObject.initOptions && activeObject.initOptions.name) { //이건 바뀔 가능성이 있음 if (activeObject.initOptions.name.indexOf('guide') > -1) { contextType = 'surface' //면형상 diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 46709a76..df0dc0d2 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1213,13 +1213,44 @@ export function useMode() { } const templateSideMode = () => { - changeMode(canvas, Mode.EDIT) - if (historyPoints.current.length >= 4) { - const wall = drawWallPolygon() - setWall(wall) + const firstPoint = historyPoints.current[0] + const lastPoint = historyPoints.current[historyPoints.current.length - 1] + historyPoints.current.forEach((point) => { + canvas?.remove(point) + }) + drawLineWithLength(lastPoint, firstPoint) - console.log('sideWall', wall) + // 캔버스에서 모든 라인 객체를 찾습니다. + const lines = historyLines.current + historyLines.current = [] + + // 각 라인의 시작점과 끝점을 사용하여 다각형의 점 배열을 생성합니다. + const points = lines.map((line) => ({ x: line.x1, y: line.y1 })) + + // 모든 라인 객체를 캔버스에서 제거합니다. + lines.forEach((line) => { + canvas?.remove(line) + }) + + // 점 배열을 사용하여 새로운 다각형 객체를 생성합니다. + const polygon = new QPolygon( + points, + { + stroke: 'black', + fill: 'transparent', + viewLengthText: true, + fontSize: 15, + selectable: true, + }, + canvas, + ) + + // 새로운 다각형 객체를 캔버스에 추가합니다. + canvas.add(polygon) + + changeMode(canvas, Mode.DEFAULT) + return polygon } } From bd44c9e19f21472f97e075c8a818bba26ce8038a Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 20 Sep 2024 10:45:42 +0900 Subject: [PATCH 2/6] =?UTF-8?q?=EB=A7=88=EC=9A=B0=EC=8A=A4=20=EC=9D=B4?= =?UTF-8?q?=EB=B2=A4=ED=8A=B8=20=EC=98=A4=EB=8F=99=EC=9E=91=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasMenu.jsx | 51 ++++++++++++++++--- src/components/floor-plan/MenuDepth01.jsx | 31 ++++++----- .../modal/outerlinesetting/OuterLineWall.jsx | 8 ++- src/hooks/useEvent.js | 24 +++++++-- 4 files changed, 88 insertions(+), 26 deletions(-) diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index c916a0fc..d4b7e40b 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -5,19 +5,24 @@ import MenuDepth01 from './MenuDepth01' import QSelectBox from '@/components/common/select/QSelectBox' import { useMessage } from '@/hooks/useMessage' import { post } from '@/lib/Axios' -import { useRecoilState, useRecoilValue } from 'recoil' +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom' -import { canvasZoomState, verticalHorizontalModeState } from '@/store/canvasAtom' +import { canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom' +import { outerLinePointsState } from '@/store/outerLineAtom' +import { MENU } from '@/common/common' export default function CanvasMenu(props) { const [objectNo] = useState('test123240912001') // 이후 삭제 필요 const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal } = props const [menuNumber, setMenuNumber] = useState(null) const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) - const [vertical, setVertical] = useState(true) const [type, setType] = useState('') const { getMessage } = useMessage() const canvasZoom = useRecoilValue(canvasZoomState) + const canvas = useRecoilValue(canvasState) + const setCurrentMenu = useSetRecoilState(currentMenuState) + const setPoints = useSetRecoilState(outerLinePointsState) + const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] const onClickNav = (number) => { setMenuNumber(number) @@ -91,6 +96,11 @@ export default function CanvasMenu(props) { } } + const handleClear = () => { + setPoints([]) + canvas?.clear() + } + return (
@@ -111,6 +121,7 @@ export default function CanvasMenu(props) { className={`canvas-menu-item ${menuNumber === 2 ? 'active' : ''}`} onClick={() => { onClickNav(2) + setCurrentMenu(MENU.ROOF_COVERING.DEFAULT) }} > -
  • onClickNav(3)}> +
  • { + onClickNav(3) + setCurrentMenu(MENU.BATCH_CANVAS.DEFAULT) + }} + >
  • -
  • onClickNav(4)}> +
  • { + onClickNav(4) + setCurrentMenu(MENU.MODULE_CIRCUIT_SETTING.DEFAULT) + }} + >
  • -
  • onClickNav(5)}> +
  • { + onClickNav(5) + setCurrentMenu(MENU.ESTIMATE.DEFAULT) + }} + >
  • -
  • onClickNav(6)}> +
  • { + onClickNav(6) + setCurrentMenu(MENU.POWER_GENERATION_SIMULATION.DEFAULT) + }} + >
  • - +
    diff --git a/src/components/floor-plan/MenuDepth01.jsx b/src/components/floor-plan/MenuDepth01.jsx index cf0ad1e8..98ea5d2b 100644 --- a/src/components/floor-plan/MenuDepth01.jsx +++ b/src/components/floor-plan/MenuDepth01.jsx @@ -3,14 +3,19 @@ import { ToggleonMouse } from '@/components/header/Header' import { useMessage } from '@/hooks/useMessage' import { useEffect, useState } from 'react' +import { MENU } from '@/common/common' +import { currentMenuState } from '@/store/canvasAtom' +import { useSetRecoilState } from 'recoil' export default function MenuDepth01(props) { const { setShowOutlineModal, type } = props const { getMessage } = useMessage() const [activeMenu, setActiveMenu] = useState() - const onClickMenu = (menuNum) => { - setActiveMenu(menuNum) - setShowOutlineModal(menuNum === 0) + const setCurrentMenu = useSetRecoilState(currentMenuState) + const onClickMenu = ({ id, menu, name }) => { + setActiveMenu(id) + setShowOutlineModal(menu === MENU.ROOF_COVERING.EXTERIOR_WALL_LINE) + setCurrentMenu(menu) } const menus = [ @@ -26,21 +31,21 @@ export default function MenuDepth01(props) { const menuInfo = { outline: [ // 지붕덮개 - { id: 0, name: 'plan.menu.roof.cover.outline.drawing' }, - { id: 1, name: 'plan.menu.roof.cover.roof.shape.setting' }, - { id: 2, name: 'plan.menu.roof.cover.roof.shape.edit' }, - { id: 3, name: 'plan.menu.roof.cover.auxiliary.line.drawing' }, + { id: 0, name: 'plan.menu.roof.cover.outline.drawing', menu: MENU.ROOF_COVERING.EXTERIOR_WALL_LINE }, + { id: 1, name: 'plan.menu.roof.cover.roof.shape.setting', menu: MENU.ROOF_COVERING.ROOF_SHAPE_SETTINGS }, + { id: 2, name: 'plan.menu.roof.cover.roof.shape.edit', menu: MENU.ROOF_COVERING.ROOF_SHAPE_EDITING }, + { id: 3, name: 'plan.menu.roof.cover.auxiliary.line.drawing', menu: MENU.ROOF_COVERING.HELP_LINE_DRAWING }, ], surface: [ // 배치면 - { id: 0, name: 'plan.menu.placement.surface.drawing' }, - { id: 1, name: 'plan.menu.placement.surface.surface' }, - { id: 2, name: 'plan.menu.placement.surface.object' }, + { id: 0, name: 'plan.menu.placement.surface.drawing', menu: MENU.BATCH_CANVAS.BATCH_DRAWING }, + { id: 1, name: 'plan.menu.placement.surface.surface', menu: MENU.BATCH_CANVAS.SURFACE_SHAPE_BATCH }, + { id: 2, name: 'plan.menu.placement.surface.object', menu: MENU.BATCH_CANVAS.OBJECT_BATCH }, ], module: [ // 모듈, 회로 구성 - { id: 0, name: 'plan.menu.module.circuit.setting.default' }, - { id: 1, name: 'plan.menu.module.circuit.setting.circuit.trestle.setting' }, + { id: 0, name: 'plan.menu.module.circuit.setting.default', menu: MENU.MODULE_CIRCUIT_SETTING.BASIC_SETTING }, + { id: 1, name: 'plan.menu.module.circuit.setting.circuit.trestle.setting', menu: MENU.MODULE_CIRCUIT_SETTING.CIRCUIT_TRESTLE_SETTING }, ], } @@ -55,7 +60,7 @@ export default function MenuDepth01(props) { {menuInfo[type].map((menu) => { return (
  • - +
  • ) })} diff --git a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx index 05d7cb0c..229bf54c 100644 --- a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx @@ -22,7 +22,8 @@ import { distanceBetweenPoints } from '@/util/canvas-util' export default function OuterLineWall(props) { const { setShowOutlineModal } = props const { getMessage } = useMessage() - const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners } = useEvent() + const { addCanvasMouseEventListener, addDocumentEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, removeMouseEvent } = + useEvent() const { addLineText, removeLineText } = useLine() const verticalHorizontalMode = useRecoilValue(verticalHorizontalModeState) @@ -40,8 +41,11 @@ export default function OuterLineWall(props) { const canvas = useRecoilValue(canvasState) useEffect(() => { - removeAllMouseEventListeners() + removeMouseEvent('mouse:down', mouseDown) addCanvasMouseEventListener('mouse:down', mouseDown) + return () => { + removeAllMouseEventListeners() + } }, [verticalHorizontalMode, points]) useEffect(() => { diff --git a/src/hooks/useEvent.js b/src/hooks/useEvent.js index f3b81115..c41b189f 100644 --- a/src/hooks/useEvent.js +++ b/src/hooks/useEvent.js @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react' -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' +import { useRecoilState, useRecoilValue } from 'recoil' import { canvasState, canvasZoomState, currentMenuState } from '@/store/canvasAtom' import { fabric } from 'fabric' @@ -8,7 +8,7 @@ export function useEvent() { const currentMenu = useRecoilValue(currentMenuState) const keyboardEventListeners = useRef([]) const mouseEventListeners = useRef([]) - const setCanvasZoom = useSetRecoilState(canvasZoomState) + const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState) useEffect(() => { if (!canvas) { @@ -16,6 +16,13 @@ export function useEvent() { } removeAllMouseEventListeners() removeAllDocumentEventListeners() + + /** + * wheelEvent + */ + canvas?.off('mouse:wheel') + canvas?.on('mouse:wheel', wheelEvent) + addDefaultEvent() }, [currentMenu, canvas]) @@ -23,7 +30,7 @@ export function useEvent() { //default Event 추가 addCanvasMouseEventListener('mouse:move', defaultMouseMoveEvent) addCanvasMouseEventListener('mouse:out', defaultMouseOutEvent) - addCanvasMouseEventListener('mouse:wheel', wheelEvent) + addDocumentEventListener('keydown', document, defaultKeyboardEvent) } @@ -126,10 +133,21 @@ export function useEvent() { keyboardEventListeners.current.length = 0 // 배열 초기화 } + const removeMouseEvent = (type, handler) => { + mouseEventListeners.current = mouseEventListeners.current.filter((event) => { + if (event.type === type && event.handler === handler) { + canvas.off(type, handler) + return false + } + return true + }) + } + return { addDocumentEventListener, addCanvasMouseEventListener, removeAllMouseEventListeners, removeAllDocumentEventListeners, + removeMouseEvent, } } From 993870e1694d77584ebd36f39d508fb8b5edcb63 Mon Sep 17 00:00:00 2001 From: changkyu choi Date: Fri, 20 Sep 2024 16:15:01 +0900 Subject: [PATCH 3/6] =?UTF-8?q?Canvas=20=EC=84=A4=EC=A0=95=20=ED=99=94?= =?UTF-8?q?=EB=A9=B4=20=EC=88=98=EC=A0=95(globalLocale=20=EB=B0=8F=20toast?= =?UTF-8?q?Up=20=EC=A0=81=EC=9A=A9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/floor-plan/CanvasMenu.jsx | 34 +++++++++---- .../modal/setting01/FirstOption.jsx | 48 +++++++++---------- .../modal/setting01/SecondOption.jsx | 47 +++++++++--------- 3 files changed, 69 insertions(+), 60 deletions(-) diff --git a/src/components/floor-plan/CanvasMenu.jsx b/src/components/floor-plan/CanvasMenu.jsx index d4b7e40b..e7323076 100644 --- a/src/components/floor-plan/CanvasMenu.jsx +++ b/src/components/floor-plan/CanvasMenu.jsx @@ -4,15 +4,21 @@ import { useEffect, useState } from 'react' import MenuDepth01 from './MenuDepth01' import QSelectBox from '@/components/common/select/QSelectBox' import { useMessage } from '@/hooks/useMessage' -import { post } from '@/lib/Axios' import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' import { settingModalFirstOptionsState, settingModalSecondOptionsState } from '@/store/settingAtom' import { canvasState, canvasZoomState, currentMenuState, verticalHorizontalModeState } from '@/store/canvasAtom' import { outerLinePointsState } from '@/store/outerLineAtom' +import { appMessageStore, globalLocaleStore } from '@/store/localeAtom' import { MENU } from '@/common/common' +import { useAxios } from '@/hooks/useAxios' +import { toastUp } from '@/hooks/useToast' + +import KO from '@/locales/ko.json' +import JA from '@/locales/ja.json' + export default function CanvasMenu(props) { - const [objectNo] = useState('test123240912001') // 이후 삭제 필요 + const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 const { setShowCanvasSettingModal, showOutlineModal, setShowOutlineModal } = props const [menuNumber, setMenuNumber] = useState(null) const [verticalHorizontalMode, setVerticalHorizontalMode] = useRecoilState(verticalHorizontalModeState) @@ -22,6 +28,9 @@ export default function CanvasMenu(props) { const canvas = useRecoilValue(canvasState) const setCurrentMenu = useSetRecoilState(currentMenuState) const setPoints = useSetRecoilState(outerLinePointsState) + const globalLocale = useRecoilValue(globalLocaleStore) + const [appMessageState, setAppMessageState] = useRecoilState(appMessageStore) + const { post } = useAxios() const SelectOption = [{ name: '瓦53A' }, { name: '瓦53A' }] const onClickNav = (number) => { @@ -38,7 +47,13 @@ export default function CanvasMenu(props) { const firstOptions = useRecoilState(settingModalFirstOptionsState) const secondOptions = useRecoilState(settingModalSecondOptionsState) - useEffect(() => {}, [menuNumber, type]) + useEffect(() => { + if (globalLocale === 'k') { + setAppMessageState(KO) + } else { + setAppMessageState(JA) + } + }, [menuNumber, type, globalLocale]) // 저장버튼(btn08) 클릭 시 호출되는 함수 const handleSaveSettings = async () => { @@ -65,6 +80,7 @@ export default function CanvasMenu(props) { const patternData = { objectNo, + //디스플레이 설정(다중) assignDisplay: dataToSend.firstOption1[0].selected, drawDisplay: dataToSend.firstOption1[1].selected, gridDisplay: dataToSend.firstOption1[2].selected, @@ -76,9 +92,11 @@ export default function CanvasMenu(props) { trestleDisplay: dataToSend.firstOption1[8].selected, coordiDisplay: dataToSend.firstOption1[9].selected, drawConverDisplay: dataToSend.firstOption1[10].selected, + //화면 표시(다중) onlyBorder: dataToSend.firstOption2[0].selected, lineHatch: dataToSend.firstOption2[1].selected, allPainted: dataToSend.firstOption2[2].selected, + //흡착범위 설정(단건) adsorpRangeSmall: dataToSend.secondOption2[0].selected, adsorpRangeSmallSemi: dataToSend.secondOption2[1].selected, adsorpRangeMedium: dataToSend.secondOption2[2].selected, @@ -86,13 +104,11 @@ export default function CanvasMenu(props) { } // HTTP POST 요청 보내기 - await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }) - - // 응답 처리 - alert('설정이 저장되었습니다.') + await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }).then((res) => { + toastUp({ message: res.returnMessage, type: 'success' }) + }) } catch (error) { - console.error('설정을 저장하는 동안 오류가 발생했습니다:', error) - alert('설정을 저장하는 중 오류가 발생했습니다.') + toastUp({ message: res.returnMessage, type: 'error' }) } } diff --git a/src/components/floor-plan/modal/setting01/FirstOption.jsx b/src/components/floor-plan/modal/setting01/FirstOption.jsx index 8c292a05..e0c4170d 100644 --- a/src/components/floor-plan/modal/setting01/FirstOption.jsx +++ b/src/components/floor-plan/modal/setting01/FirstOption.jsx @@ -1,28 +1,24 @@ import { useRecoilState } from 'recoil' import { settingModalFirstOptionsState } from '@/store/settingAtom' import { useMessage } from '@/hooks/useMessage' -import React, { useEffect, useState } from 'react' -import { get } from '@/lib/Axios' +import React, { useCallback, useEffect, useState } from 'react' +import { useAxios } from '@/hooks/useAxios' export default function FirstOption() { - const [objectNo] = useState('test123240912001') // 이후 삭제 필요 + const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 const [settingsModalOptions, setSettingModalOptions] = useRecoilState(settingModalFirstOptionsState) const { option1, option2 } = settingsModalOptions const { getMessage } = useMessage() - - const [isFetched, setIsFetched] = useState(false) // 조회 여부 상태 + const { get } = useAxios() // 데이터를 최초 한 번만 조회 useEffect(() => { console.log('FirstOption useEffect 실행') - if (!isFetched) { - // 조회가 안 되었을 때만 fetchSettings 실행 - fetchSettings() - } - }, [isFetched]) // isFetched 상태가 변할 때마다 확인 + fetchSettings() + }, [objectNo]) // Canvas Setting 조회 및 초기화 - const fetchSettings = async () => { + const fetchSettings = useCallback(async () => { try { const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` }) @@ -49,12 +45,10 @@ export default function FirstOption() { option1, option2, }) - - setIsFetched(true) // 조회가 완료되면 isFetched를 true로 설정 } catch (error) { console.error('Data fetching error:', error) } - } + }, [objectNo]) const onClickOption = (option) => { option.selected = !option.selected @@ -67,23 +61,25 @@ export default function FirstOption() {

    {getMessage('modal.canvas.setting.first.option.info')}

    - {settingsModalOptions?.option1?.map((item) => ( - - ))} + {settingsModalOptions && + settingsModalOptions.option1.map((item) => ( + + ))}

    {getMessage('modal.canvas.setting.first.option.display')}

    - {settingsModalOptions?.option2?.map((item) => ( - - ))} + {settingsModalOptions && + settingsModalOptions.option2.map((item) => ( + + ))}
    diff --git a/src/components/floor-plan/modal/setting01/SecondOption.jsx b/src/components/floor-plan/modal/setting01/SecondOption.jsx index 0a568b67..7b8d8811 100644 --- a/src/components/floor-plan/modal/setting01/SecondOption.jsx +++ b/src/components/floor-plan/modal/setting01/SecondOption.jsx @@ -1,27 +1,24 @@ import { useRecoilState } from 'recoil' import { settingModalSecondOptionsState } from '@/store/settingAtom' import { useMessage } from '@/hooks/useMessage' -import React, { useEffect, useState } from 'react' -import { get } from '@/lib/Axios' +import React, { useCallback, useEffect, useState } from 'react' +import { useAxios } from '@/hooks/useAxios' export default function SecondOption() { - const [objectNo] = useState('test123240912001') // 이후 삭제 필요 + const [objectNo, setObjectNo] = useState('test123240912001') // 이후 삭제 필요 const [settingsModalOptions, setSettingModalOptions] = useRecoilState(settingModalSecondOptionsState) const { option1, option2 } = settingsModalOptions const { getMessage } = useMessage() - - const [isFetched, setIsFetched] = useState(false) // 조회 여부 상태 + const { get } = useAxios() // 데이터를 최초 한 번만 조회 useEffect(() => { console.log('SecondOption useEffect 실행') - if (!isFetched) { - // 조회가 안 되었을 때만 fetchSettings 실행 - fetchSettings() - } - }, [isFetched]) // isFetched 상태가 변할 때마다 확인 + fetchSettings() + }, [objectNo]) - const fetchSettings = async () => { + // Canvas Setting 조회 및 초기화 + const fetchSettings = useCallback(async () => { try { const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` }) @@ -36,12 +33,10 @@ export default function SecondOption() { option1, option2, }) - - setIsFetched(true) // 조회가 완료되면 isFetched를 true로 설정 } catch (error) { console.error('Data fetching error:', error) } - } + }, [objectNo]) const onClickOption = (option) => { // option2에서 한 개만 선택 가능하도록 처리 @@ -54,22 +49,24 @@ export default function SecondOption() {

    {getMessage('modal.canvas.setting.font.plan.edit')}

    - {settingsModalOptions.option1.map((item, index) => ( - - ))} + {settingsModalOptions && + settingsModalOptions.option1.map((item) => ( + + ))}

    {getMessage('modal.canvas.setting.font.plan.absorption')}

    - {settingsModalOptions.option2.map((item, index) => ( - - ))} + {settingsModalOptions && + settingsModalOptions.option2.map((item) => ( + + ))}
    ) diff --git a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx index 229bf54c..0548c73c 100644 --- a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx @@ -8,6 +8,7 @@ import { useEvent } from '@/hooks/useEvent' import { canvasState, verticalHorizontalModeState } from '@/store/canvasAtom' import { OUTER_LINE_TYPE, + outerLineAngle1State, outerLineArrow1State, outerLineArrow2State, outerLineLength1State, @@ -18,6 +19,7 @@ import { import { QLine } from '@/components/fabric/QLine' import { useLine } from '@/hooks/useLine' import { distanceBetweenPoints } from '@/util/canvas-util' +import { calculateAngle } from '@/util/qpolygon-utils' export default function OuterLineWall(props) { const { setShowOutlineModal } = props @@ -29,6 +31,7 @@ export default function OuterLineWall(props) { 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) @@ -38,6 +41,8 @@ export default function OuterLineWall(props) { const arrow1Ref = useRef(arrow1) const arrow2Ref = useRef(arrow2) + const [angle1, setAngle1] = useRecoilState(outerLineAngle1State) + const canvas = useRecoilValue(canvasState) useEffect(() => { @@ -168,6 +173,24 @@ export default function OuterLineWall(props) { } 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, @@ -178,7 +201,12 @@ export default function OuterLineWall(props) { canvas?.add(line) addLineText(line) } else { - const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], { + const nearPoint = + distanceBetweenPoints(firstPoint, { x: lastPoint.x, y: firstPoint.y }) <= + distanceBetweenPoints(firstPoint, { x: firstPoint.x, y: lastPoint.y }) + ? [lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y] + : [lastPoint.x, lastPoint.y, firstPoint.x, lastPoint.y] + const guideLine1 = new QLine(nearPoint, { stroke: 'grey', strokeWidth: 1, strokeDashArray: [1, 1, 1], @@ -427,7 +455,25 @@ export default function OuterLineWall(props) { console.log('leegubae') }, angle: (e) => { - console.log('angle') + 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') @@ -446,6 +492,27 @@ export default function OuterLineWall(props) { 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) => { if (prev.length === 0) { return [] @@ -453,6 +520,7 @@ export default function OuterLineWall(props) { return [...prev, { x: prev[0].x, y: prev[0].y }] }) } + return (
    @@ -505,7 +573,8 @@ export default function OuterLineWall(props) { value={length1} ref={length1Ref} onChange={(e) => { - setLength1(e.target.value.replace(/[^-0-9]/g, '')) + const value = e.target.value.replace(/^0+/, '') + setLength1(value.replace(/[^-0-9]/g, '')) }} placeholder="3000" /> @@ -525,7 +594,8 @@ export default function OuterLineWall(props) { value={length1} ref={length1Ref} onChange={(e) => { - setLength1(e.target.value.replace(/[^-0-9]/g, '')) + const value = e.target.value.replace(/^0+/, '') + setLength1(value.replace(/[^-0-9]/g, '')) }} placeholder="3000" /> @@ -542,7 +612,8 @@ export default function OuterLineWall(props) { value={length2} ref={length2Ref} onChange={(e) => { - setLength2(e.target.value.replace(/[^-0-9]/g, '')) + const value = e.target.value.replace(/^0+/, '') + setLength2(value.replace(/[^-0-9]/g, '')) }} placeholder="3000" /> @@ -552,6 +623,44 @@ export default function OuterLineWall(props) {
    + ) : type === OUTER_LINE_TYPE.ANGLE ? ( +
    +
    + + { + const value = e.target.value.replace(/^0+/, '') + setLength1(value.replace(/[^-0-9]/g, '')) + }} + placeholder="3000" + /> +
    +
    + + { + const val = e.target.value + // const pattern = /(^\d+$)|(^\d{1,}.\d{0,2}$)/ + const pattern = /^-?(\d{1,3}([.]\d{0,2})?)?$/ + if (!pattern.test(val)) { + // prev에서 마지막 자리 제거 + setAngle1(val.slice(0, val.length - 1)) + return + } + + setAngle1(val) + }} + className="input-origin block" + /> +
    +
    ) : ( <> )} From 3cacc2dcaac58f68c289a8a7d73e7660e7542088 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 20 Sep 2024 17:50:29 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=EB=B3=B4=EC=A1=B0=EC=84=A0=20=EC=9B=90?= =?UTF-8?q?=EB=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../floor-plan/modal/outerlinesetting/OuterLineWall.jsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx index 0548c73c..52056e39 100644 --- a/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx +++ b/src/components/floor-plan/modal/outerlinesetting/OuterLineWall.jsx @@ -201,12 +201,7 @@ export default function OuterLineWall(props) { canvas?.add(line) addLineText(line) } else { - const nearPoint = - distanceBetweenPoints(firstPoint, { x: lastPoint.x, y: firstPoint.y }) <= - distanceBetweenPoints(firstPoint, { x: firstPoint.x, y: lastPoint.y }) - ? [lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y] - : [lastPoint.x, lastPoint.y, firstPoint.x, lastPoint.y] - const guideLine1 = new QLine(nearPoint, { + const guideLine1 = new QLine([lastPoint.x, lastPoint.y, lastPoint.x, firstPoint.y], { stroke: 'grey', strokeWidth: 1, strokeDashArray: [1, 1, 1],