From b7536de37a77b94840e24876437a7e7700cc7283 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Thu, 22 Aug 2024 16:03:30 +0900 Subject: [PATCH 1/7] =?UTF-8?q?=EC=BB=A8=ED=85=8D=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9C=A0=ED=8B=B8=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=EB=A1=9C=20=EB=B3=80=EA=B2=BD,=20=EA=B7=B8?= =?UTF-8?q?=EB=A6=AC=EB=93=9C=20=EC=84=A4=EC=A0=95=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 28 ++++++++- src/components/SettingsModal.jsx | 57 +++++++++++++++++++ .../common/context-menu/QContextMenu.jsx} | 17 +++--- 3 files changed, 92 insertions(+), 10 deletions(-) create mode 100644 src/components/SettingsModal.jsx rename src/{util/context-util.js => components/common/context-menu/QContextMenu.jsx} (86%) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 102a7bea..4f3f110d 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -3,7 +3,7 @@ import { useCanvas } from '@/hooks/useCanvas' import { useEffect, useState, useRef } from 'react' import { Mode, useMode } from '@/hooks/useMode' -import { Button } from '@nextui-org/react' +import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input } from '@nextui-org/react' import RangeSlider from './ui/RangeSlider' import { useRecoilState, useRecoilValue } from 'recoil' import { @@ -21,7 +21,10 @@ import { getCanvasState, insertCanvasState } from '@/lib/canvas' import { calculateIntersection } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import ThumbnailList from './ui/ThumbnailLIst' -import CanvasWithContextMenu from '@/util/context-util' +// import CanvasWithContextMenu from '@/util/context-util' +import QContextMenu from './common/context-menu/QContextMenu' +import { modalContent, modalState } from '@/store/modalAtom' +import SettingsModal from './SettingsModal' export default function Roof2() { const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas') @@ -55,6 +58,15 @@ export default function Roof2() { const wall = useRecoilValue(wallState) + const [open, setOpen] = useRecoilState(modalState) + const [contents, setContent] = useRecoilState(modalContent) + + // const gridHandleClick = (e) => { + // console.log(e !== 'custom') + + // setIsCustomGridSetting(e !== 'custom') + // } + //canvas 썸네일 const [thumbnails, setThumbnails] = useState([]) const thumbnailProps = { @@ -530,6 +542,16 @@ export default function Roof2() { {canvas && ( <>
+ @@ -705,7 +727,7 @@ export default function Roof2() {
- {canvas !== undefined && } + {canvas !== undefined && }
) diff --git a/src/components/SettingsModal.jsx b/src/components/SettingsModal.jsx new file mode 100644 index 00000000..ec3b5b23 --- /dev/null +++ b/src/components/SettingsModal.jsx @@ -0,0 +1,57 @@ +import { useEffect, useState } from 'react' +import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input } from '@nextui-org/react' + +export default function SettingsModal() { + const [isCustomGridSetting, setIsCustomGridSetting] = useState(true) + const [ratioValue, setRatioValue] = useState('원치수') + + useEffect(() => { + setIsCustomGridSetting(ratioValue !== 'custom') + }, [ratioValue]) + + return ( + <> +
+

점 그리드 설정

+ +
+ + mm +
+ +

Selected: {ratioValue}

+ + + 원치수 + 1/2 + 1/4 + 1/10 + 임의간격 + + + 종횡연동 + +
+ + mm +
+
+ + mm +
+
+ + + +
+
+ + ) +} diff --git a/src/util/context-util.js b/src/components/common/context-menu/QContextMenu.jsx similarity index 86% rename from src/util/context-util.js rename to src/components/common/context-menu/QContextMenu.jsx index 0179fdf0..57aaf45b 100644 --- a/src/util/context-util.js +++ b/src/components/common/context-menu/QContextMenu.jsx @@ -1,10 +1,15 @@ -import React, { useState, useEffect, forwardRef, useContext } from 'react' +'use client' +import { useEffect, useState } from 'react' +import { useRecoilState, useRecoilValue } from 'recoil' -const CanvasWithContextMenu = forwardRef(({ canvasProps }, ref) => { +export default function QContextMenu(props) { + const { contextRef, canvasProps } = props + + // const children = useRecoilValue(modalContent) const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 }) useEffect(() => { - if (!ref.current) return + if (!contextRef.current) return const handleContextMenu = (e) => { e.preventDefault() //기존 contextmenu 막고 @@ -34,7 +39,7 @@ const CanvasWithContextMenu = forwardRef(({ canvasProps }, ref) => { document.removeEventListener('click', handleClick) document.removeEventListener('click', handleOutsideClick) } - }, [ref, contextMenu]) + }, [contextRef, contextMenu]) const handleMenuClick = (option) => { alert(`option ${option} clicked`) @@ -60,6 +65,4 @@ const CanvasWithContextMenu = forwardRef(({ canvasProps }, ref) => { )} ) -}) - -export default CanvasWithContextMenu +} From a9ee0bc08c6a2e789df865093a3105597c2010d3 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Fri, 23 Aug 2024 14:19:11 +0900 Subject: [PATCH 2/7] =?UTF-8?q?=EA=B0=80=EC=9D=B4=EB=93=9C=20=EB=9D=BC?= =?UTF-8?q?=EC=9D=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 15 +- src/components/SettingsModal.jsx | 273 +++++++++++++++++++++++++++---- src/hooks/useMode.js | 2 +- 3 files changed, 243 insertions(+), 47 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 4f3f110d..2a153f07 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -2,8 +2,9 @@ import { useCanvas } from '@/hooks/useCanvas' import { useEffect, useState, useRef } from 'react' -import { Mode, useMode } from '@/hooks/useMode' -import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input } from '@nextui-org/react' +import { useMode } from '@/hooks/useMode' +import { Mode } from '@/common/common' +import { Button } from '@nextui-org/react' import RangeSlider from './ui/RangeSlider' import { useRecoilState, useRecoilValue } from 'recoil' import { @@ -21,7 +22,6 @@ import { getCanvasState, insertCanvasState } from '@/lib/canvas' import { calculateIntersection } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import ThumbnailList from './ui/ThumbnailLIst' -// import CanvasWithContextMenu from '@/util/context-util' import QContextMenu from './common/context-menu/QContextMenu' import { modalContent, modalState } from '@/store/modalAtom' import SettingsModal from './SettingsModal' @@ -61,12 +61,6 @@ export default function Roof2() { const [open, setOpen] = useRecoilState(modalState) const [contents, setContent] = useRecoilState(modalContent) - // const gridHandleClick = (e) => { - // console.log(e !== 'custom') - - // setIsCustomGridSetting(e !== 'custom') - // } - //canvas 썸네일 const [thumbnails, setThumbnails] = useState([]) const thumbnailProps = { @@ -544,9 +538,8 @@ export default function Roof2() {
- - -
+
+
+ + mm +
+
+ + mm +
+
+ + +
) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 07acbb9c..337ccbc8 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -67,7 +67,7 @@ export function useMode() { useEffect(() => { // 이벤트 리스너 추가 // if (!canvas) { - canvas?.setZoom(0.8) + // canvas?.setZoom(0.8) // return // } document.addEventListener('keydown', handleKeyDown) From dfeadd5d1756a4c7f0788101f6a0903d159ccce3 Mon Sep 17 00:00:00 2001 From: yjnoh Date: Fri, 23 Aug 2024 15:31:47 +0900 Subject: [PATCH 3/7] =?UTF-8?q?=EA=B7=B8=EB=A6=AC=EB=93=9C=20recoil=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 3 +++ src/components/SettingsModal.jsx | 30 ++++++++++++++++++++++-------- src/hooks/useMode.js | 5 +++++ src/store/canvasAtom.js | 6 ++++++ 4 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index d24a2398..13cecc35 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -26,6 +26,7 @@ import ThumbnailList from './ui/ThumbnailLIst' import QContextMenu from './common/context-menu/QContextMenu' import { modalContent, modalState } from '@/store/modalAtom' import SettingsModal from './SettingsModal' +import { get } from '@/lib/Axios' export default function Roof2(props) { const { name, userId, email, isLoggedIn } = props @@ -63,6 +64,8 @@ export default function Roof2(props) { const [open, setOpen] = useRecoilState(modalState) const [contents, setContent] = useRecoilState(modalContent) + const [scale, setScale] = useState(1) + //canvas 썸네일 const [thumbnails, setThumbnails] = useState([]) const thumbnailProps = { diff --git a/src/components/SettingsModal.jsx b/src/components/SettingsModal.jsx index 0d1b3c79..b263b47b 100644 --- a/src/components/SettingsModal.jsx +++ b/src/components/SettingsModal.jsx @@ -2,6 +2,7 @@ import { useEffect, useRef, useState } from 'react' import { Button, Checkbox, CheckboxGroup, RadioGroup, Radio, Input } from '@nextui-org/react' import { useRecoilState, useRecoilValue } from 'recoil' import { modalContent, modalState } from '@/store/modalAtom' +import { guideLineState } from '@/store/canvasAtom' import { fabric } from 'fabric' export default function SettingsModal(props) { @@ -12,12 +13,15 @@ export default function SettingsModal(props) { const moduleLength = useRef(90) //모듈 mm 길이 입력 const [open, setOpen] = useRecoilState(modalState) + const [guideLine, setGuideLine] = useRecoilState(guideLineState) useEffect(() => { setIsCustomGridSetting(ratioValue !== 'custom') }, [ratioValue]) const drawGridSettings = () => { + const gridSettingArray = [] + if (gridCheckedValue.includes('line')) { // fabric.GuideRect = fabric.util.createClass(fabric.Rect, { // _render: function (ctx) { @@ -98,20 +102,15 @@ export default function SettingsModal(props) { verticalLineArray.push(verticalLine) } canvasProps.renderAll() - const snapDistance = 10 - console.log('horizontalLineArray', horizontalLineArray) + const snapDistance = 10 canvasProps.on('mouse:move', function (e) { let mouseObj = e const mouseCursorX = e.pointer.x const mouseCursorY = e.pointer.y - console.log(`mouseCursorX : ${mouseCursorX}, mouseCursorY : ${mouseCursorY}`) - horizontalLineArray.forEach((line) => { - console.log() - if (Math.abs(mouseCursorY - line.y1) < snapDistance) { mouseObj.x = mouseCursorX mouseObj.y = line.y1 @@ -154,11 +153,19 @@ export default function SettingsModal(props) { // } // }) // }) + + const recoilObj = { + guideMode: 'guideLine', + horizontalLineArray, + verticalLineArray, + moduleLength: moduleLength.current.value, + } + gridSettingArray.push(recoilObj) } if (gridCheckedValue.includes('dot')) { const circle = new fabric.Circle({ - radius: 5, + radius: 2, fill: 'red', originX: 'center', originY: 'center', @@ -204,9 +211,16 @@ export default function SettingsModal(props) { ) canvasProps.add(backgroundPolygon) canvasProps.renderAll() - } + const recoilObj = { + guideMode: 'guideDot', + moduleLength: moduleLength.current.value, + } + + gridSettingArray.push(recoilObj) + } canvasProps.renderAll() + setGuideLine(gridSettingArray) } return ( diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 77d9de25..936579b2 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -23,6 +23,7 @@ import { sortedPolygonArray, templateTypeState, wallState, + guideLineState, } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { fabric } from 'fabric' @@ -64,6 +65,8 @@ export function useMode() { const compass = useRecoilValue(compassState) const [isCellCenter, setIsCellCenter] = useState(false) + const guideLine = useRecoilValue(guideLineState) + useEffect(() => { // 이벤트 리스너 추가 // if (!canvas) { @@ -104,6 +107,8 @@ export function useMode() { }, [mode, isGuidePointMode]) const drawMouseLines = (e) => { + console.log('guideLine', guideLine) + // 현재 마우스 포인터의 위치를 가져옵니다. const pointer = canvas?.getPointer(e.e) diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index b1bacbdb..062698c0 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -88,3 +88,9 @@ export const compassState = atom({ default: undefined, dangerouslyAllowMutability: true, }) + +export const guideLineState = atom({ + key: 'guideLine', + default: {}, + dangerouslyAllowMutability: true, +}) From ebb97fe063f37bb9ec984885b725ce418b4fc31b Mon Sep 17 00:00:00 2001 From: yoosangwook Date: Fri, 23 Aug 2024 16:20:07 +0900 Subject: [PATCH 4/7] feat: Add useMessage hook for localization support --- src/components/Playground.jsx | 3 +++ src/hooks/useMessage.js | 38 +++++++++++++++++++++++++++++++++++ src/locales/ja.json | 3 +++ src/locales/ko.json | 3 +++ src/store/localeAtom.js | 6 ++++++ 5 files changed, 53 insertions(+) create mode 100644 src/hooks/useMessage.js create mode 100644 src/locales/ja.json create mode 100644 src/locales/ko.json create mode 100644 src/store/localeAtom.js diff --git a/src/components/Playground.jsx b/src/components/Playground.jsx index 2467798a..e1043d2b 100644 --- a/src/components/Playground.jsx +++ b/src/components/Playground.jsx @@ -3,6 +3,7 @@ import { Button, Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react' import { useAxios } from '@/hooks/useAxios' +import { useMessage } from '@/hooks/useMessage' // import { get } from '@/lib/Axios' import QSelect from '@/components/ui/QSelect' @@ -12,6 +13,7 @@ import styles from './playground.module.css' export default function Playground() { const { get } = useAxios() const testVar = process.env.NEXT_PUBLIC_TEST + const { getMessage } = useMessage() const handleUsers = async () => { // const users = await get('/api/user/find-all') @@ -58,6 +60,7 @@ export default function Playground() {

Sass 테스트입니다.

+
{getMessage('hi')}
) diff --git a/src/hooks/useMessage.js b/src/hooks/useMessage.js new file mode 100644 index 00000000..bc2f7eef --- /dev/null +++ b/src/hooks/useMessage.js @@ -0,0 +1,38 @@ +import { useRecoilValue } from 'recoil' +import { globalLocaleState } from '@/store/localeAtom' + +import KO from '@/locales/ko.json' +import JA from '@/locales/ja.json' + +const SESSION_STORAGE_MESSAGE_KEY = 'QCAST_MESSAGE_STORAGE' + +export const useMessage = () => { + const globalLocale = useRecoilValue(globalLocaleState) + + const getMessage = (key, args = []) => { + if (sessionStorage.getItem(SESSION_STORAGE_MESSAGE_KEY) === null) { + if (globalLocale === 'ko') { + setSessionMessage(JSON.stringify(KO)) + } else { + setSessionMessage(JSON.stringify(JA)) + } + } + + const sessionMessage = getSessionMessage() + const message = sessionMessage[key] || key + + return args.reduce((acc, arg, i) => { + return acc.replaceAll(`{${i}}`, arg) + }, message) + } + + const setSessionMessage = (sessionMessage) => { + sessionStorage.setItem(SESSION_STORAGE_MESSAGE_KEY, sessionMessage) + } + + const getSessionMessage = () => { + return JSON.parse(sessionStorage.getItem(SESSION_STORAGE_MESSAGE_KEY)) + } + + return { getMessage } +} diff --git a/src/locales/ja.json b/src/locales/ja.json new file mode 100644 index 00000000..a1725dfd --- /dev/null +++ b/src/locales/ja.json @@ -0,0 +1,3 @@ +{ + "hi": "こんにちは" +} diff --git a/src/locales/ko.json b/src/locales/ko.json new file mode 100644 index 00000000..282d9722 --- /dev/null +++ b/src/locales/ko.json @@ -0,0 +1,3 @@ +{ + "hi": "안녕하세요" +} diff --git a/src/store/localeAtom.js b/src/store/localeAtom.js new file mode 100644 index 00000000..d82bcf7b --- /dev/null +++ b/src/store/localeAtom.js @@ -0,0 +1,6 @@ +import { atom } from 'recoil' + +export const globalLocaleState = atom({ + key: 'globalLocaleState', + default: 'ko', +}) From 8fe2960f92a9c0b9a9c35b77f2a37afc40ede2f3 Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 23 Aug 2024 16:38:16 +0900 Subject: [PATCH 5/7] =?UTF-8?q?=EA=B7=B8=EB=A6=AC=EB=93=9C=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=ED=9B=84=20=EC=97=90=EB=94=94=ED=8C=85=20=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=20=EB=B3=80=EA=B2=BD=20=EC=8B=9C=20=EA=B0=80=EA=B9=8C?= =?UTF-8?q?=EC=9A=B4=20line=EC=97=90=20=EB=B6=99=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Roof2.jsx | 4 +- src/hooks/useMode.js | 126 +++++++++++++++++++++++---------------- src/util/canvas-util.js | 32 +++++++++- 3 files changed, 110 insertions(+), 52 deletions(-) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index 13cecc35..7467a634 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -26,12 +26,14 @@ import ThumbnailList from './ui/ThumbnailLIst' import QContextMenu from './common/context-menu/QContextMenu' import { modalContent, modalState } from '@/store/modalAtom' import SettingsModal from './SettingsModal' -import { get } from '@/lib/Axios' +import { useAxios } from '@/hooks/useAxios' export default function Roof2(props) { const { name, userId, email, isLoggedIn } = props const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas') + const { get } = useAxios() + const canvasRef = useRef(null) //canvas 기본 사이즈 diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 936579b2..ffca9f48 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -4,6 +4,8 @@ import { distanceBetweenPoints, findTopTwoIndexesByDistance, getCenterPoint, + getClosestHorizontalLine, + getClosestVerticalLine, getDirection, getStartIndex, rearrangeArray, @@ -42,7 +44,6 @@ export function useMode() { const [canvas, setCanvas] = useState(null) const [zoom, setZoom] = useState(100) const [fontSize] = useRecoilState(fontSizeState) - const [shape, setShape] = useState(0) const [sortedArray, setSortedArray] = useRecoilState(sortedPolygonArray) const [roof, setRoof] = useRecoilState(roofState) const [wall, setWall] = useRecoilState(wallState) @@ -65,7 +66,7 @@ export function useMode() { const compass = useRecoilValue(compassState) const [isCellCenter, setIsCellCenter] = useState(false) - const guideLine = useRecoilValue(guideLineState) + const guideLineInfo = useRecoilValue(guideLineState) useEffect(() => { // 이벤트 리스너 추가 @@ -107,7 +108,27 @@ export function useMode() { }, [mode, isGuidePointMode]) const drawMouseLines = (e) => { - console.log('guideLine', guideLine) + let isGuideLineMode = false, + isGuideDotMode = false + let guideDotLength, guideLineLength, horizontalLineArray, verticalLineArray + + if (isObjectNotEmpty(guideLineInfo)) { + const guideLineState = guideLineInfo.filter((item) => item.guideMode === 'guideLine') + const guideDotState = guideLineInfo.filter((item) => item.guideMode === 'guideDot') + + isGuideLineMode = guideLineState.length > 0 + isGuideDotMode = guideDotState.length > 0 + + if (isGuideDotMode) { + guideDotLength = Number(guideDotState[0].moduleLength) + } + + if (isGuideLineMode) { + horizontalLineArray = [...guideLineState[0].horizontalLineArray] + verticalLineArray = [...guideLineState[0].verticalLineArray] + guideLineLength = Number(guideLineState[0].moduleLength) + } + } // 현재 마우스 포인터의 위치를 가져옵니다. const pointer = canvas?.getPointer(e.e) @@ -115,21 +136,56 @@ export function useMode() { // 기존에 그려진 가이드라인을 제거합니다. removeMouseLines() - if (canvas?.getActiveObject()) { - return - } - let newX, newY - if (isGuidePointMode && mode === Mode.EDIT) { - newX = Math.round(pointer.x / 200) * 200 - newY = Math.round(pointer.y / 200) * 200 - } else { - newX = pointer.x - newY = pointer.y + let newX = pointer.x + let newY = pointer.y + + if (isGuideLineMode && isGuideDotMode && mode === Mode.EDIT) { + } else if (isGuideDotMode && mode === Mode.EDIT) { + const x = pointer.x - guideDotLength * Math.floor(pointer.x / guideDotLength) + const y = pointer.y - guideDotLength * Math.floor(pointer.y / guideDotLength) + + const xRate = x / guideDotLength + const yRate = y / guideDotLength + const isAttachX = xRate >= 0.4 && xRate <= 0.7 + const isAttachY = yRate >= 0.4 && yRate <= 0.7 + + if (isAttachX && isAttachY) { + newX = Math.floor(pointer.x / guideDotLength) * guideDotLength + guideDotLength / 2 + newY = Math.floor(pointer.y / guideDotLength) * guideDotLength + guideDotLength / 2 + } + } else if (isGuideLineMode && mode === Mode.EDIT) { + const closestHorizontalLine = getClosestHorizontalLine(pointer, horizontalLineArray) + const closetVerticalLine = getClosestVerticalLine(pointer, verticalLineArray) + const xDiff = Math.abs(pointer.x - closetVerticalLine.x1) + const yDiff = Math.abs(pointer.y - closestHorizontalLine.y1) + + const x = pointer.x - guideLineLength * Math.floor(pointer.x / guideLineLength) + const y = pointer.y - guideLineLength * Math.floor(pointer.y / guideLineLength) + + const xRate = x / guideLineLength + const yRate = y / guideLineLength + const isAttachX = xRate >= 0.4 && xRate <= 0.7 + const isAttachY = yRate >= 0.4 && yRate <= 0.7 + + if (isAttachX && isAttachY) { + newX = Math.floor(pointer.x / guideLineLength) * guideLineLength + guideLineLength / 2 + newY = Math.floor(pointer.y / guideLineLength) * guideLineLength + guideLineLength / 2 + } else { + if (Math.min(xDiff, yDiff) <= 20) { + if (xDiff < yDiff) { + newX = closetVerticalLine.x1 + newY = pointer.y + } else { + newX = pointer.x + newY = closestHorizontalLine.y1 + } + } + } } // 가로선을 그립니다. const horizontalLine = new fabric.Line([0, newY, canvasSize.horizontal, newY], { - stroke: 'black', + stroke: 'red', strokeWidth: 1, selectable: false, name: 'mouseLine', @@ -137,7 +193,7 @@ export function useMode() { // 세로선을 그립니다. const verticalLine = new fabric.Line([newX, 0, newX, canvasSize.vertical], { - stroke: 'black', + stroke: 'red', strokeWidth: 1, selectable: false, name: 'mouseLine', @@ -762,10 +818,8 @@ export function useMode() { points.current = [] historyPoints.current = [] - // handleOuterlines() const wall = makePolygon(null, sort) wall.set({ name: 'wall' }) - console.log('wall', wall) return wall } @@ -804,33 +858,7 @@ export function useMode() { const makePolygon = (otherLines, sort = true) => { // 캔버스에서 모든 라인 객체를 찾습니다. const lines = otherLines || historyLines.current - - if (!otherLines) { - //외각선 기준 - const topIndex = findTopTwoIndexesByDistance(sortedArray) //배열중에 큰 2값을 가져옴 TODO: 나중에는 인자로 받아서 다각으로 수정 해야됨 - - //일단 배열 6개 짜리 기준의 선 번호 - if (topIndex[0] === 4) { - if (topIndex[1] === 5) { - //1번 - setShape(1) - } - } else if (topIndex[0] === 1) { - //4번 - if (topIndex[1] === 2) { - setShape(4) - } - } else if (topIndex[0] === 0) { - if (topIndex[1] === 1) { - //2번 - setShape(2) - } else if (topIndex[1] === 5) { - setShape(3) - } - } - - historyLines.current = [] - } + historyLines.current = [] // 각 라인의 시작점과 끝점을 사용하여 다각형의 점 배열을 생성합니다. const points = lines.map((line) => ({ x: line.x1, y: line.y1 })) @@ -858,12 +886,10 @@ export function useMode() { canvas.add(polygon) // 캔버스를 다시 그립니다. - if (!otherLines) { - // polygon.fillCell() - canvas?.renderAll() - // polygon.setViewLengthText(false) - setMode(Mode.DEFAULT) - } + // polygon.fillCell() + canvas?.renderAll() + // polygon.setViewLengthText(false) + setMode(Mode.DEFAULT) return polygon } diff --git a/src/util/canvas-util.js b/src/util/canvas-util.js index 7374c024..268370c1 100644 --- a/src/util/canvas-util.js +++ b/src/util/canvas-util.js @@ -38,7 +38,7 @@ export function actionHandler(eventData, transform, x, y) { // define a function that can keep the polygon in the same position when we change its width/height/top/left export function anchorWrapper(anchorIndex, fn) { - return function(eventData, transform, x, y) { + return function (eventData, transform, x, y) { let fabricObject = transform.target let originX = fabricObject?.points[anchorIndex].x - fabricObject.pathOffset.x let originY = fabricObject.points[anchorIndex].y - fabricObject.pathOffset.y @@ -675,3 +675,33 @@ export function findClosestPointWithDifferentXY(targetPoint, points) { return closestPoint } + +export const getClosestHorizontalLine = (pointer, horizontalLineArray) => { + let closestLine = null + let minDistance = Infinity + + horizontalLineArray.forEach((line) => { + const distance = Math.abs(line.y1 - pointer.y) // Assuming horizontal lines have the same y1 and y2 + if (distance < minDistance) { + minDistance = distance + closestLine = line + } + }) + + return closestLine +} + +export const getClosestVerticalLine = (pointer, verticalLineArray) => { + let closestLine = null + let minDistance = Infinity + + verticalLineArray.forEach((line) => { + const distance = Math.abs(line.x1 - pointer.x) // Assuming horizontal lines have the same y1 and y2 + if (distance < minDistance) { + minDistance = distance + closestLine = line + } + }) + + return closestLine +} From 3ab518e1881f353697c7aa7982537e070795b4af Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 23 Aug 2024 16:42:17 +0900 Subject: [PATCH 6/7] =?UTF-8?q?=EB=91=98=EB=8B=A4=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=EC=8B=9C=EB=8F=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index ffca9f48..3b24d85f 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -140,6 +140,33 @@ export function useMode() { let newY = pointer.y if (isGuideLineMode && isGuideDotMode && mode === Mode.EDIT) { + const closestHorizontalLine = getClosestHorizontalLine(pointer, horizontalLineArray) + const closetVerticalLine = getClosestVerticalLine(pointer, verticalLineArray) + const xDiff = Math.abs(pointer.x - closetVerticalLine.x1) + const yDiff = Math.abs(pointer.y - closestHorizontalLine.y1) + + const x = pointer.x - guideLineLength * Math.floor(pointer.x / guideLineLength) + const y = pointer.y - guideLineLength * Math.floor(pointer.y / guideLineLength) + + const xRate = x / guideLineLength + const yRate = y / guideLineLength + const isAttachX = xRate >= 0.4 && xRate <= 0.7 + const isAttachY = yRate >= 0.4 && yRate <= 0.7 + + if (isAttachX && isAttachY) { + newX = Math.floor(pointer.x / guideLineLength) * guideLineLength + guideLineLength / 2 + newY = Math.floor(pointer.y / guideLineLength) * guideLineLength + guideLineLength / 2 + } else { + if (Math.min(xDiff, yDiff) <= 20) { + if (xDiff < yDiff) { + newX = closetVerticalLine.x1 + newY = pointer.y + } else { + newX = pointer.x + newY = closestHorizontalLine.y1 + } + } + } } else if (isGuideDotMode && mode === Mode.EDIT) { const x = pointer.x - guideDotLength * Math.floor(pointer.x / guideDotLength) const y = pointer.y - guideDotLength * Math.floor(pointer.y / guideDotLength) From 39e8dc335e430ef53f9964b924a8abadd22de31a Mon Sep 17 00:00:00 2001 From: "hyojun.choi" Date: Fri, 23 Aug 2024 17:01:57 +0900 Subject: [PATCH 7/7] =?UTF-8?q?=EC=97=90=EB=94=94=ED=8C=85=20=EB=AA=A8?= =?UTF-8?q?=EB=93=9C=20=EC=8B=9C=20=EC=83=9D=EC=84=B1=EB=90=98=EB=8A=94=20?= =?UTF-8?q?line=20name=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/hooks/useMode.js | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index 3b24d85f..11cc0ec2 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -68,6 +68,9 @@ export function useMode() { const guideLineInfo = useRecoilValue(guideLineState) + const [guideLineMode, setGuideLineMode] = useState(false) + const [guideDotMode, setGuideDotMode] = useState(false) + useEffect(() => { // 이벤트 리스너 추가 // if (!canvas) { @@ -105,7 +108,19 @@ export function useMode() { canvas?.off('mouse:down') canvas?.on('mouse:down', mouseEvent.editMode) } - }, [mode, isGuidePointMode]) + }, [mode]) + + useEffect(() => { + setGuideLineMode(false) + setGuideDotMode(false) + if (isObjectNotEmpty(guideLineInfo)) { + const guideLineState = guideLineInfo.filter((item) => item.guideMode === 'guideLine') + const guideDotState = guideLineInfo.filter((item) => item.guideMode === 'guideDot') + + setGuideLineMode(guideLineState.length > 0) + setGuideDotMode(guideDotState.length > 0) + } + }, [guideLineInfo]) const drawMouseLines = (e) => { let isGuideLineMode = false, @@ -115,7 +130,8 @@ export function useMode() { if (isObjectNotEmpty(guideLineInfo)) { const guideLineState = guideLineInfo.filter((item) => item.guideMode === 'guideLine') const guideDotState = guideLineInfo.filter((item) => item.guideMode === 'guideDot') - + setGuideLineMode(guideLineState.length > 0) + setGuideDotMode(guideDotState.length > 0) isGuideLineMode = guideLineState.length > 0 isGuideDotMode = guideDotState.length > 0 @@ -242,7 +258,7 @@ export function useMode() { }, [pointCount.current]) const removeGuideLines = () => { - const guideLines = canvas?._objects.filter((obj) => obj.name === 'guideLine') + const guideLines = canvas?._objects.filter((obj) => obj.name === 'helpGuideLine') guideLines?.forEach((item) => canvas?.remove(item)) } @@ -264,7 +280,7 @@ export function useMode() { strokeWidth: 1, strokeDashArray: [1, 1, 1], }) - guideLine.name = 'guideLine' + guideLine.name = 'helpGuideLine' canvas?.add(guideLine) } else { const guideLine1 = new QLine([lastX, lastY, lastX, arrivalY], { @@ -281,8 +297,8 @@ export function useMode() { strokeDashArray: [1, 1, 1], }) - guideLine1.name = 'guideLine' - guideLine2.name = 'guideLine' + guideLine1.name = 'helpGuideLine' + guideLine2.name = 'helpGuideLine' canvas?.add(guideLine1) canvas?.add(guideLine2) @@ -675,7 +691,7 @@ export function useMode() { canvas?.add(circle) if (points.current.length === 2) { - if (isGuidePointMode) { + if (guideLineMode || guideDotMode) { const vector = { x: points.current[1].left - points.current[0].left, y: points.current[1].top - points.current[0].top,