diff --git a/.env.development b/.env.development index 4c2c816c..9a1529ed 100644 --- a/.env.development +++ b/.env.development @@ -1,6 +1,6 @@ NEXT_PUBLIC_TEST="테스트변수입니다. development" -NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080" + NEXT_PUBLIC_API_SERVER_PATH="http://1.248.227.176:38080" # NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080" DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;password=Qwertqaz12345;trustServerCertificate=true" diff --git a/package.json b/package.json index cfafa961..7a9f1077 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ }, "devDependencies": { "@turf/turf": "^7.0.0", + "dayjs": "^1.11.13", "postcss": "^8", "prettier": "^3.3.3", "prisma": "^5.18.0", diff --git a/qcast-front.iml b/qcast-front.iml new file mode 100644 index 00000000..bef2a966 --- /dev/null +++ b/qcast-front.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/src/app/[locale]/settings/page.jsx b/src/app/[locale]/settings/page.jsx new file mode 100644 index 00000000..797c024c --- /dev/null +++ b/src/app/[locale]/settings/page.jsx @@ -0,0 +1,16 @@ +import Hero from '@/components/Hero' +import Settings from '@/components/Settings' +import { initCheck } from '@/util/session-util' + +export default async function SettingsPage() { + await initCheck() + + return ( + <> + +
+ +
+ + ) +} diff --git a/src/components/GridSettingsModal.jsx b/src/components/GridSettingsModal.jsx index 78e3c8a6..f1e77c94 100644 --- a/src/components/GridSettingsModal.jsx +++ b/src/components/GridSettingsModal.jsx @@ -2,7 +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 { guideLineState, horiGuideLinesState, vertGuideLinesState } from '@/store/canvasAtom' import { fabric } from 'fabric' import { ColorPicker, useColor } from 'react-color-palette' import 'react-color-palette/css' @@ -18,6 +18,8 @@ export default function SettingsModal(props) { const [open, setOpen] = useRecoilState(modalState) const [guideLine, setGuideLine] = useRecoilState(guideLineState) + const [horiGuideLines, setHoriGuideLines] = useRecoilState(horiGuideLinesState) + const [vertGuideLines, setVertGuideLines] = useRecoilState(vertGuideLinesState) const gridSettingArray = [] @@ -76,6 +78,7 @@ export default function SettingsModal(props) { name: 'guideLine', strokeDashArray: [5, 2], opacity: 0.3, + direction: 'horizontal', }, ) canvasProps.add(horizontalLine) @@ -97,6 +100,7 @@ export default function SettingsModal(props) { name: 'guideLine', strokeDashArray: [5, 2], opacity: 0.3, + direction: 'vertical', }, ) canvasProps.add(verticalLine) @@ -114,6 +118,16 @@ export default function SettingsModal(props) { moduleHoriLength: moduleHoriLength, } gridSettingArray.push(recoilObj) + const newHoriGuideLines = [...horiGuideLines] + horizontalLineArray.forEach((line) => { + newHoriGuideLines.push(line) + }) + const newVertGuideLines = [...vertGuideLines] + verticalLineArray.forEach((line) => { + newVertGuideLines.push(line) + }) + setHoriGuideLines(newHoriGuideLines) + setVertGuideLines(newVertGuideLines) } if (gridCheckedValue.includes('dot')) { @@ -187,6 +201,8 @@ export default function SettingsModal(props) { guideLines?.forEach((item) => canvasProps.remove(item)) canvasProps.renderAll() setGuideLine([]) + setHoriGuideLines([]) + setVertGuideLines([]) } else { alert('그리드가 없습니다.') return diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index ec47b201..83ca1213 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -1,7 +1,7 @@ 'use client' import { useCanvas } from '@/hooks/useCanvas' -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { v4 as uuidv4 } from 'uuid' import { useMode } from '@/hooks/useMode' import { Mode } from '@/common/common' @@ -21,7 +21,7 @@ import { } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { getCanvasState, insertCanvasState } from '@/lib/canvas' -import { calculateIntersection } from '@/util/canvas-util' +import { calculateIntersection, distanceBetweenPoints } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' import ThumbnailList from './ui/ThumbnailLIst' import QContextMenu from './common/context-menu/QContextMenu' @@ -30,6 +30,7 @@ import { useAxios } from '@/hooks/useAxios' import QPolygonContextMenu from '@/components/common/context-menu/QPolygonContextMenu' import QLineContextMenu from '@/components/common/context-menu/QLineContextMenu' import QEmptyContextMenu from '@/components/common/context-menu/QEmptyContextMenu' +import { degreesToRadians, radiansToDegrees } from '@turf/turf' import InitSettingsModal from './InitSettingsModal' import GridSettingsModal from './GridSettingsModal' @@ -573,6 +574,1151 @@ export default function Roof2(props) { canvas?.renderAll() } + const createTemplate1 = () => { + const length1 = prompt('1번') + const length2 = prompt('2번') + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x, y: pointer.y - parseInt(length2) / 2 }, + { x: pointer.x - parseInt(length1) / 2, y: pointer.y + parseInt(length1) / 2 }, + { x: pointer.x + parseInt(length1) / 2, y: pointer.y + parseInt(length1) / 2 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + + const createPentagon2 = () => { + const a = 400 //Number(prompt('a')) + const b = 200 //Number(prompt('b')) + const c = 250 //Number(prompt('c')) + const d = 150 //Number(prompt('d')) + + const t = (c * (a - b)) / (200 + c) + + const t2 = Math.sqrt(c * c + t * t) + + const t3 = Math.sqrt((c - d) * (c - d) + (a - b - t) * (a - b - t)) + + const angle = Math.atan(t2 / t) + const angle2 = Math.atan(t3 / (a - b - t)) + + let isDrawing = true + let pentagon + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + + pentagon = new QPolygon( + [ + { x: pointer.x - a / 2, y: pointer.y + a / 2 - d }, + { x: pointer.x + a / 2, y: pointer.y + a / 2 - d }, + { x: pointer.x + a / 2, y: pointer.y + a / 2 - d - d }, + { x: pointer.x + a / 2 - b, y: pointer.y + a / 2 - d - d }, + { + x: pointer.x + a / 2 - b - t3 * Math.cos(angle), + y: pointer.y + a / 2 - d - d - t3 * Math.sin(angle), + }, + /*{ + x: pointer.x - a / 2 + t2 * Math.cos(angle), + y: pointer.y + a / 2 - d - t2 * Math.sin(angle), + },*/ + ], + { + fill: 'transparent', + stroke: 'gray', + strokeWidth: 1, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(pentagon) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + pentagon.set('name', 'roof') + pentagon.set('stroke', 2) + canvas?.renderAll() + }) + } + + const createTemplate2 = () => { + const length1 = prompt('1번') + const length2 = prompt('2번') + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x + length1 / 2, y: pointer.y - length2 / 2 }, + { x: pointer.x - length1 / 2, y: pointer.y - length2 / 2 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + + const createTemplate3 = () => { + const length1 = Number(prompt('1번')) + const length2 = Number(prompt('2번')) + const length3 = Number(prompt('3번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x + length3 / 2, y: pointer.y - length2 / 2 }, + { x: pointer.x - length3 / 2, y: pointer.y - length2 / 2 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + + const createTemplate4 = () => { + const length1 = Number(prompt('1번')) + const length2 = Number(prompt('2번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x + length1 / 2, y: pointer.y - length2 / 2 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + + const createTemplate5 = () => { + const length1 = Number(prompt('1번')) + const length2 = Number(prompt('2번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x - length1 / 2, y: pointer.y - length2 / 2 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + + const createTemplate6 = () => { + const length1 = Number(prompt('1번')) + const length2 = Number(prompt('2번')) + const length3 = Number(prompt('3번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y - length3 / 2 }, + { x: pointer.x - length1 / 2, y: pointer.y + length3 / 2 }, + { x: pointer.x + length1 / 2, y: pointer.y + length3 / 2 }, + { x: pointer.x + length1 / 2, y: pointer.y + length3 / 2 - length2 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate7 = () => { + const length1 = Number(prompt('1번')) + const length2 = Number(prompt('2번')) + const length3 = Number(prompt('3번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y - length2 / 2 }, + { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 - length3 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate8 = () => { + const length1 = Number(prompt('1번')) // 밑변 + const length2 = Number(prompt('2번')) // 높이 + const length3 = Number(prompt('3번')) // 빗변 + + const angleInRadians = Math.asin(length2 / length3) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x - length1 / 2 + length3 * Math.cos(angleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians) }, + { x: pointer.x + length1 / 2 + length3 * Math.cos(angleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians) }, + { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate9 = () => { + const length1 = Number(prompt('1번')) // 밑변 + const length2 = Number(prompt('2번')) // 높이 + const length3 = Number(prompt('3번')) // 빗변 + + const angleInRadians = Math.asin(length2 / length3) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x + length1 / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x + length1 / 2 - length3 * Math.cos(angleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians) }, + { x: pointer.x - length1 / 2 - length3 * Math.cos(angleInRadians), y: pointer.y + length2 / 2 - length3 * Math.sin(angleInRadians) }, + { x: pointer.x - length1 / 2, y: pointer.y + length2 / 2 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate10 = () => { + const length1 = Number(prompt('1번')) + const length2 = Number(prompt('2번')) + const length3 = Number(prompt('3번')) + const length4 = Number(prompt('4번')) + const length5 = Number(prompt('5번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - (length1 + length2 + length3) / 2, y: pointer.y - (length4 + length5) / 2 }, + { x: pointer.x - (length1 + length2 + length3) / 2, y: pointer.y + (length4 + length5) / 2 }, + { x: pointer.x - (length1 + length2 + length3) / 2 + length1, y: pointer.y + (length4 + length5) / 2 }, + { x: pointer.x - (length1 + length2 + length3) / 2 + length1, y: pointer.y + (length4 + length5) / 2 - length5 }, + { x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2, y: pointer.y + (length4 + length5) / 2 - length5 }, + { x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2, y: pointer.y + (length4 + length5) / 2 - length5 + length5 }, + { + x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2 + length3, + y: pointer.y + (length4 + length5) / 2 - length5 + length5, + }, + { + x: pointer.x - (length1 + length2 + length3) / 2 + length1 + length2 + length3, + y: pointer.y + (length4 + length5) / 2 - length5 + length5 - (length4 + length5), + }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate11 = () => { + const length1 = 200 //Number(prompt('1번')) + const length2 = 100 //Number(prompt('2번')) + const length3 = 400 //Number(prompt('3번')) + const length4 = 300 //Number(prompt('4번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length3 }, + { x: pointer.x - length1 / 2 + length1 - (length1 - length2), y: pointer.y + length4 / 2 - length3 }, + { x: pointer.x - length1 / 2 + length1 - (length1 - length2), y: pointer.y + length4 / 2 - length3 + (length3 - length4) }, + { x: pointer.x - length1 / 2 + length1 - (length1 - length2) - length2, y: pointer.y + length4 / 2 - length3 + (length3 - length4) }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate12 = () => { + const length1 = 200 //Number(prompt('1번')) + const length2 = 100 //Number(prompt('2번')) + const length3 = 400 //Number(prompt('3번')) + const length4 = 300 //Number(prompt('4번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 }, + { x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length4 }, + { x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length4 - (length3 - length4) }, + { x: pointer.x - length1 / 2 + length1 - length2 - (length1 - length2), y: pointer.y + length4 / 2 - length4 - (length3 - length4) }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate13 = () => { + const length1 = 300 //Number(prompt('1번')) + const length2 = 150 //Number(prompt('2번')) + const length3 = 100 //Number(prompt('3번')) + const length4 = 400 //Number(prompt('4번')) + const length5 = 200 //Number(prompt('5번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 }, + { x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 }, + { x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + (length4 - length5) }, + { x: pointer.x - length1 / 2 + length1 - length3 - length2, y: pointer.y + length4 / 2 - length4 + (length4 - length5) }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate14 = () => { + const length1 = 300 //Number(prompt('1번')) + const length2 = 150 //Number(prompt('2번')) + const length3 = 100 //Number(prompt('3번')) + const length4 = 400 //Number(prompt('4번')) + const length5 = 200 //Number(prompt('5번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x + length1 / 2, y: pointer.y + length4 / 2 }, + { x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 }, + { x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length4 }, + { x: pointer.x + length1 / 2 - length1 + length3, y: pointer.y + length4 / 2 - length4 }, + { x: pointer.x + length1 / 2 - length1 + length3, y: pointer.y + length4 / 2 - length4 + (length4 - length5) }, + { x: pointer.x + length1 / 2 - length1 + length3 + length2, y: pointer.y + length4 / 2 - length4 + (length4 - length5) }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate15 = () => { + const length1 = 300 //Number(prompt('1번')) + const length2 = 150 //Number(prompt('2번')) + const length3 = 100 //Number(prompt('3번')) + const length4 = 400 //Number(prompt('4번')) + const length5 = 200 //Number(prompt('5번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x + length1 / 2, y: pointer.y + length4 / 2 }, + { x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 }, + { x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length5 }, + { x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 }, + { x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 - (length4 - length5) }, + { x: pointer.x + length1 / 2 - length1 + length2 + length3, y: pointer.y + length4 / 2 - length5 - (length4 - length5) }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate16 = () => { + const length1 = 300 //Number(prompt('1번')) + const length2 = 150 //Number(prompt('2번')) + const length3 = 100 //Number(prompt('3번')) + const length4 = 400 //Number(prompt('4번')) + const length5 = 200 //Number(prompt('5번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length5 }, + { x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length5 }, + { x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length5 - (length4 - length5) }, + { x: pointer.x - length1 / 2 + length1 - length2 - length3, y: pointer.y + length4 / 2 - length5 - (length4 - length5) }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate17 = () => { + const length1 = 300 //Number(prompt('1번')) + const length2 = 150 //Number(prompt('2번')) + const length3 = 250 //Number(prompt('3번')) + const length4 = 400 //Number(prompt('4번')) + const length5 = 200 //Number(prompt('5번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length5 }, + { x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length5 }, + { x: pointer.x - length1 / 2 + length1 - length2, y: pointer.y + length4 / 2 - length5 - (length4 - length5) }, + { x: pointer.x - length1 / 2 + length1 - length2 - length3, y: pointer.y + length4 / 2 - length5 - (length4 - length5) }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate18 = () => { + const length1 = 300 //Number(prompt('1번')) + const length2 = 150 //Number(prompt('2번')) + const length3 = 250 //Number(prompt('3번')) + const length4 = 400 //Number(prompt('4번')) + const length5 = 200 //Number(prompt('5번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x + length1 / 2, y: pointer.y + length4 / 2 }, + { x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 }, + { x: pointer.x + length1 / 2 - length1, y: pointer.y + length4 / 2 - length5 }, + { x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 }, + { x: pointer.x + length1 / 2 - length1 + length2, y: pointer.y + length4 / 2 - length5 - (length4 - length5) }, + { x: pointer.x + length1 / 2 - length1 + length2 + length3, y: pointer.y + length4 / 2 - length5 - (length4 - length5) }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + /** + * 19~22번은 못함 + */ + const createTemplate23 = () => { + const length1 = 300 //Number(prompt('1번')) + const length2 = 100 //Number(prompt('2번')) + const length3 = 150 //Number(prompt('3번')) + const length4 = 400 //Number(prompt('4번')) + const length5 = 300 //Number(prompt('5번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2 + length2, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 }, + { x: pointer.x - length1 / 2, y: pointer.y + length4 / 2 - length4 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 }, + { x: pointer.x - length1 / 2 + length1, y: pointer.y + length4 / 2 - length4 + length4 }, + { x: pointer.x - length1 / 2 + length1 - length3, y: pointer.y + length4 / 2 - length4 + length4 }, + { x: pointer.x - length1 / 2 + length2 + (length1 - length2 - length3) / 2, y: pointer.y + length4 / 2 - length4 + length5 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + + const createTemplate24 = () => { + const length1 = 300 //Number(prompt('1번')) + const length2 = 400 //Number(prompt('2번')) + const length3 = 300 //Number(prompt('3번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 }, + { x: pointer.x - length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 }, + { x: pointer.x, y: pointer.y + length2 - length2 / 2 - length3 - (length2 - length3) }, + { x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 }, + { x: pointer.x + length1 / 2, y: pointer.y + length2 - length2 / 2 - length3 + length3 }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate25 = () => { + const length1 = 300 //Number(prompt('1번')) + const length2 = 200 //Number(prompt('2번')) + const length3 = 300 //Number(prompt('3번')) + const length4 = 200 //Number(prompt('3번')) + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { + x: pointer.x - length1 / 2, + y: pointer.y + length3 / 2, + }, + { + x: pointer.x - length1 / 2 + (length1 - length2) / 2, + y: pointer.y + length3 / 2 - (length3 - length4), + }, + { + x: pointer.x - length1 / 2 + (length1 - length2) / 2, + y: pointer.y + length3 / 2 - (length3 - length4) - length4, + }, + { + x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2, + y: pointer.y + length3 / 2 - (length3 - length4) - length4, + }, + { + x: pointer.x - length1 / 2 + (length1 - length2) / 2 + length2, + y: pointer.y + length3 / 2 - (length3 - length4) - length4 + length4, + }, + { + x: pointer.x - length1 / 2 + length1, + y: pointer.y + length3 / 2, + }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate26 = () => { + const length1 = 500 //Number(prompt('1번')) + const length2 = 200 //Number(prompt('2번')) + const length3 = 300 //Number(prompt('3번')) + const length4 = 400 //Number(prompt('3번')) + + const angle = (Math.asin(length3 / length4) * 180) / Math.PI // 높이와 빗변으로 먼저 각도구하기 + + const topL = (length1 - length2) / 2 / Math.cos((angle * Math.PI) / 180) // 꺽이는부분 윗쪽 길이 + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { + x: pointer.x - length1 / 2 + length1, + y: pointer.y + length3 / 2, + }, + { + x: pointer.x - length1 / 2, + y: pointer.y + length3 / 2, + }, + { + x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)), + y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), + }, + { + x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2, + y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), + }, + { + x: pointer.x - length1 / 2 + length4 * Math.cos(degreesToRadians(angle)) + length2 + topL * Math.cos(degreesToRadians(angle)), + y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)) + topL * Math.sin(degreesToRadians(angle)), + }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate27 = () => { + const length1 = 500 //Number(prompt('1번')) + const length2 = 200 //Number(prompt('2번')) + const length3 = 300 //Number(prompt('3번')) + const length4 = 400 //Number(prompt('3번')) + + const angle = (Math.asin(length3 / length4) * 180) / Math.PI // 높이와 빗변으로 먼저 각도구하기 + + const topL = (length1 - length2) / 2 / Math.cos((angle * Math.PI) / 180) // 꺽이는부분 윗쪽 길이 + + let isDrawing = true + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const triangle = new QPolygon( + [ + { + x: pointer.x - length1 / 2, + y: pointer.y + length3 / 2, + }, + { + x: pointer.x - length1 / 2 + length1, + y: pointer.y + length3 / 2, + }, + { + x: pointer.x - length1 / 2 + length1 - length4 * Math.cos(degreesToRadians(angle)), + y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), + }, + { + x: pointer.x - length1 / 2 + length1 - length4 * Math.cos(degreesToRadians(angle)) - length2, + y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)), + }, + { + x: pointer.x - length1 / 2 + length1 - length4 * Math.cos(degreesToRadians(angle)) - length2 - topL * Math.cos(degreesToRadians(angle)), + y: pointer.y + length3 / 2 - length4 * Math.sin(degreesToRadians(angle)) + topL * Math.sin(degreesToRadians(angle)), + }, + ], + { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(triangle) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + }) + } + const createTemplate28 = () => { + const length1 = Number(prompt('밑변')) + const length2 = Number(prompt('높이')) + const length3 = Number(prompt('빗변')) + + const a = Math.sqrt(length3 * length3 - length2 * length2) // 입력된 밑변과 높이 + + const sinA = a / length3 + const angleInRadians = Math.asin(sinA) + const angleInDegrees = angleInRadians * (180 / Math.PI) + const b = a - length1 / 2 + + const c = b / Math.tan(angleInRadians) + const d = Math.sqrt(b * b + c * c) + + if (isNaN(a)) { + alert('값이 잘못되었습니다.') + return + } + if (b < 0) { + alert('값이 잘못되었습니다.') + return + } + if (angleInDegrees === 0 || angleInRadians === 0) { + alert('값이 잘못되었습니다.') + return + } + + let isDrawing = true + let pentagon + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const newAngleInRadians = (90 - angleInDegrees) * (Math.PI / 180) + + pentagon = new QPolygon( + [ + { x: pointer.x - (length1 + b) / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x + length1 / 2 - b / 2, y: pointer.y + length2 / 2 }, + { + x: pointer.x + length1 / 2 - b / 2 + d * Math.cos(newAngleInRadians), + y: pointer.y + length2 / 2 - d * Math.sin(newAngleInRadians), + }, + { + x: pointer.x - (length1 + b) / 2 + length3 * Math.cos(newAngleInRadians), + y: pointer.y + length2 / 2 - length3 * Math.sin(newAngleInRadians), + }, + ], + { + fill: 'transparent', + stroke: 'gray', + strokeWidth: 1, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(pentagon) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + pentagon.set('name', 'roof') + pentagon.set('stroke', 2) + canvas?.renderAll() + }) + } + const createTemplate29 = () => { + const length1 = Number(prompt('밑변')) + const length2 = Number(prompt('높이')) + const length3 = Number(prompt('빗변')) + + const a = Math.sqrt(length3 * length3 - length2 * length2) // 입력된 밑변과 높이 + + const sinA = a / length3 + const angleInRadians = Math.asin(sinA) + const angleInDegrees = angleInRadians * (180 / Math.PI) + const b = a - length1 / 2 + + const c = b / Math.tan(angleInRadians) + const d = Math.sqrt(b * b + c * c) + + if (isNaN(a)) { + alert('값이 잘못되었습니다.') + return + } + if (b < 0) { + alert('값이 잘못되었습니다.') + return + } + if (angleInDegrees === 0 || angleInRadians === 0) { + alert('값이 잘못되었습니다.') + return + } + + let isDrawing = true + let pentagon + canvas?.on('mouse:move', (e) => { + if (!isDrawing) { + return + } + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) + const pointer = canvas?.getPointer(e.e) + const newAngleInRadians = (90 - angleInDegrees) * (Math.PI / 180) + + pentagon = new QPolygon( + [ + { + x: pointer.x + length1 / 2 - b / 2 - length3 * Math.cos(newAngleInRadians), + y: pointer.y + length2 / 2 - length3 * Math.sin(newAngleInRadians), + }, + { x: pointer.x + length1 / 2 - b / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x - (length1 + b) / 2, y: pointer.y + length2 / 2 }, + { x: pointer.x - (length1 + b) / 2 - d * Math.cos(newAngleInRadians), y: pointer.y + length2 / 2 - d * Math.sin(newAngleInRadians) }, + ], + { + fill: 'transparent', + stroke: 'gray', + strokeWidth: 1, + selectable: true, + fontSize: fontSize, + name: 'guideTriangle', + }, + ) + + canvas?.add(pentagon) + }) + + canvas?.on('mouse:down', (e) => { + isDrawing = false + pentagon.set('name', 'roof') + pentagon.set('stroke', 2) + canvas?.renderAll() + }) + } + return ( <> {canvas && ( @@ -728,6 +1874,84 @@ export default function Roof2(props) { + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/Settings.jsx b/src/components/Settings.jsx new file mode 100644 index 00000000..2d8eaeda --- /dev/null +++ b/src/components/Settings.jsx @@ -0,0 +1,197 @@ +'use client' + +import React, { useEffect, useState } from 'react'; +import { Button } from '@nextui-org/react'; + +import { get, post } from '@/lib/Axios'; + +export default function Settings() { + const [objectNo, setObjectNo] = useState('test123240829010'); + const [error, setError] = useState(null); + + // 상태를 하나의 객체로 관리 + const [settings, setSettings] = useState({ + display1: Array(11).fill('N'), // 화면 표시1 + display2: Array(3).fill('N'), // 화면 표시2 + rangeSetting: 0, // 흡착 범위 설정 + gridSettings: [] // 그리드 설정 + }); + + const gridItems = { + display1: [ + '할당 표시', '도면 표시', '그리드 표시', '문자 표시', '흐름방향 표시', + '복도치수 표시', '실제치수 표시', '치수 표시 없음', '가대 표시', + '좌표 표시', '도면전환 표시' + ], + display2: ['테두리만', '라인해치', 'All Painted'], + rangeSetting: ['극소', '소', '중', '대'], + gridSettings: ['임의 그리드', '실선 그리드', '점 그리드', '그리드 색 설정', '흡착점 추가'] + }; + + // 클릭 시 상태 변경 함수 + const handleToggle = (type, index) => { + + setSettings((prevSettings) => { + + // prevSettings[type]이 배열인지 확인하고, 그렇지 않은 경우 빈 배열로 초기화 + let updated = Array.isArray(prevSettings[type]) ? [...prevSettings[type]] : []; + + if (type === 'rangeSetting') { + return { ...prevSettings, [type]: index }; + } + + updated[index] = updated[index] === 'N' ? 'Y' : 'N'; + return { ...prevSettings, [type]: updated }; + }); + }; + + // Canvas Setting 조회 및 초기화 + const handleSelect = async () => { + try { + const res = await get({ url: `/api/canvas-management/canvas-settings/by-object/${objectNo}` }); + + // 데이터가 없는 경우 + if (!res || res.length === 0) { + + console.warn('조회 결과가 없습니다.'); + // 기본값을 설정하거나 사용자에게 알림 표시 + setSettings({ + display1: Array(11).fill('N'), // 화면 표시1 기본값 + display2: Array(3).fill('N'), // 화면 표시2 기본값 + rangeSetting: 0, // 흡착 범위 설정 기본값 + gridSettings: [] // 그리드 설정 초기화 + }); + + alert('조회된 데이터가 없습니다. 기본 설정이 적용됩니다.'); + return; // 이후 코드 실행을 중단 + } + + const data = res.map((item) => ({ + display1: [ + item.assignDisplay, item.drawDisplay, item.gridDisplay, item.charDisplay, item.flowDisplay, + item.hallwayDimenDisplay, item.actualDimenDisplay, item.noDimenDisplay, item.trestleDisplay, + item.coordiDisplay, item.drawConverDisplay + ], + display2: [item.onlyBorder, item.lineHatch, item.allPainted], + rangeSetting: Number(item.adsorpRangeSetting) + })); + + setSettings({ + display1: data[0].display1, + display2: data[0].display2, + rangeSetting: data[0].rangeSetting, + gridSettings: [] // 초기화 + }); + } catch (error) { + console.error('Data fetching error:', error); + } + }; + + // Canvas Setting 저장 + const handleSubmit = async (e) => { + e.preventDefault(); + + if (!objectNo) { + alert('object_no를 입력하세요.'); + return; + } + + const patternData = { + objectNo, + assignDisplay: settings.display1[0], + drawDisplay: settings.display1[1], + gridDisplay: settings.display1[2], + charDisplay: settings.display1[3], + flowDisplay: settings.display1[4], + hallwayDimenDisplay: settings.display1[5], + actualDimenDisplay: settings.display1[6], + noDimenDisplay: settings.display1[7], + trestleDisplay: settings.display1[8], + coordiDisplay: settings.display1[9], + drawConverDisplay: settings.display1[10], + onlyBorder: settings.display2[0], + lineHatch: settings.display2[1], + allPainted: settings.display2[2], + adsorpRangeSetting: String(settings.rangeSetting) + }; + + await post({ url: `/api/canvas-management/canvas-settings`, data: patternData }); + + // 저장 후 재조회 + handleSelect(); + }; + + // 초기 조회 + useEffect(() => { + if (objectNo) { + handleSelect(objectNo); + } else { + alert('object_no를 입력하세요.'); + } + }, []); + + return ( + <> +
+
+ setObjectNo(e.target.value)} /> + + +
+
+

[디스플레이 설정]

+

* 도면에 표시할 항목을 클릭하면 적용 됩니다.

+
+ {gridItems.display1.map((item, index) => ( +
handleToggle('display1', index)} + > + {settings.display1[index]} {item} +
+ ))} +
+
+

* 화면 표시

+
+ {gridItems.display2.map((item, index) => ( +
handleToggle('display2', index)} + > + {settings.display2[index]} {item} +
+ ))} +
+

[글꼴/도면크기 설정]

+

* 흡착 범위 설정

+
+ {gridItems.rangeSetting.map((item, index) => ( +
handleToggle('rangeSetting', index)} + > + {settings.rangeSetting === index ? 'Y' : 'N'} {item} +
+ ))} +
+

[그리드 설정]

+
+ {gridItems.gridSettings.map((item, index) => ( +
handleToggle('gridSettings', index)} + > + {settings.gridSettings.includes(index) ? 'Y' : 'N'} {item} +
+ ))} +
+
+
+ + ); +} \ No newline at end of file diff --git a/src/components/fabric/HelpLine.js b/src/components/fabric/HelpLine.js deleted file mode 100644 index 68112099..00000000 --- a/src/components/fabric/HelpLine.js +++ /dev/null @@ -1,108 +0,0 @@ -import { fabric } from 'fabric' - -export class QLine extends fabric.Group { - line - text - fontSize - length = 0 - x1 - y1 - x2 - y2 - direction - type = 'HelpLine' - parent - #lengthTxt = 0 - - constructor(points, option, lengthTxt) { - const [x1, y1, x2, y2] = points - - if (!option.fontSize) { - throw new Error('Font size is required.') - } - - const line = new fabric.Line(points, { ...option, strokeWidth: 1 }) - super([line], {}) - - this.x1 = x1 - this.y1 = y1 - this.x2 = x2 - this.y2 = y2 - this.line = line - this.fontSize = option.fontSize - this.direction = option.direction - this.parent = option.parent - - if (lengthTxt > 0) { - this.#lengthTxt = Number(lengthTxt) - } - - this.#init() - this.#addControl() - } - - #init() { - this.#addLengthText(true) - } - - #addControl() { - this.on('moving', () => { - this.#addLengthText(false) - }) - - this.on('modified', (e) => { - this.#addLengthText(false) - }) - - this.on('selected', () => { - Object.keys(this.controls).forEach((controlKey) => { - if (controlKey !== 'ml' && controlKey !== 'mr') { - this.setControlVisible(controlKey, false) - } - }) - }) - } - - #addLengthText(isFirst) { - if (this.text) { - this.removeWithUpdate(this.text) - this.text = null - } - - if (isFirst && this.#lengthTxt > 0) { - const text = new fabric.Textbox(this.#lengthTxt.toFixed(0).toString(), { - left: (this.x1 + this.x2) / 2, - top: (this.y1 + this.y2) / 2, - fontSize: this.fontSize, - }) - this.length = this.#lengthTxt - this.text = text - this.addWithUpdate(text) - return - } - - const scaleX = this.scaleX - const scaleY = this.scaleY - const x1 = this.left - const y1 = this.top - const x2 = this.left + this.width * scaleX - const y2 = this.top + this.height * scaleY - const dx = x2 - x1 - const dy = y2 - y1 - this.length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(0)) - - const text = new fabric.Textbox(this.length.toFixed(0).toString(), { - left: (x1 + x2) / 2, - top: (y1 + y2) / 2, - fontSize: this.fontSize, - }) - this.text = text - this.addWithUpdate(text) - } - - setFontSize(fontSize) { - this.fontSize = fontSize - this.text.set({ fontSize }) - this.addWithUpdate() - } -} diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index c5093f91..c6da782f 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -18,6 +18,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { parentId: null, innerLines: [], children: [], + initOptions: null, initialize: function (points, options, canvas) { // 소수점 전부 제거 points.forEach((point) => { @@ -58,6 +59,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.canvas = canvas } + this.initOptions = options + this.init() this.initLines() this.setShape() @@ -177,7 +180,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { const degree = (Math.atan2(dy, dx) * 180) / Math.PI // Create new text object if it doesn't exist - const text = new fabric.Text(length.toFixed(0), { + const text = new fabric.IText(length.toFixed(0), { left: midPoint.x, top: midPoint.y, fontSize: this.fontSize, @@ -189,7 +192,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { parentDirection: getDirectionByPoint(start, end), parentDegree: degree, dirty: true, - editable: false, + editable: true, selectable: true, lockRotation: true, lockScalingX: true, diff --git a/src/hooks/useCanvas.js b/src/hooks/useCanvas.js index bcc8b7d7..0dc724f3 100644 --- a/src/hooks/useCanvas.js +++ b/src/hooks/useCanvas.js @@ -425,6 +425,8 @@ export function useCanvas(id) { 'maxY', 'minX', 'minY', + 'x', + 'y', ]) const str = JSON.stringify(objs) diff --git a/src/hooks/useCanvasEvent.js b/src/hooks/useCanvasEvent.js index 89a15b3a..e5582733 100644 --- a/src/hooks/useCanvasEvent.js +++ b/src/hooks/useCanvasEvent.js @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react' import { fabric } from 'fabric' import { useRecoilState, useRecoilValue } from 'recoil' import { canvasSizeState, currentObjectState, modeState } from '@/store/canvasAtom' +import { QPolygon } from '@/components/fabric/QPolygon' // 캔버스에 필요한 이벤트 export function useCanvasEvent() { @@ -19,9 +20,9 @@ export function useCanvasEvent() { canvas?.on('selection:cleared', selectionEvent.cleared) canvas?.on('selection:created', selectionEvent.created) canvas?.on('selection:updated', selectionEvent.updated) - canvas?.on('object:added', () => { + /*canvas?.on('object:added', () => { document.addEventListener('keydown', handleKeyDown) - }) + })*/ canvas?.on('object:removed', objectEvent.removed) } @@ -77,11 +78,55 @@ export function useCanvasEvent() { if (target.name === 'lengthText') { const x = target.left const y = target.top + // Add a property to store the previous value + const previousValue = target.text target.on('selected', (e) => { Object.keys(target.controls).forEach((controlKey) => { target.setControlVisible(controlKey, false) }) }) + target.on('editing:exited', () => { + if (isNaN(target.text.trim())) { + target.set({ text: previousValue }) + canvas?.renderAll() + return + } + const updatedValue = parseFloat(target.text.trim()) + const targetParent = target.parent + const points = targetParent.getCurrentPoints() + const i = target.idx // Assuming target.index gives the index of the point + + const startPoint = points[i] + const endPoint = points[(i + 1) % points.length] + + const dx = endPoint.x - startPoint.x + const dy = endPoint.y - startPoint.y + + const currentLength = Math.sqrt(dx * dx + dy * dy) + const scaleFactor = updatedValue / currentLength + + const newEndPoint = { + x: startPoint.x + dx * scaleFactor, + y: startPoint.y + dy * scaleFactor, + } + + const newPoints = [...points] + newPoints[(i + 1) % points.length] = newEndPoint + + for (let idx = i + 1; idx < points.length; idx++) { + if (newPoints[idx].x === endPoint.x) { + newPoints[idx].x = newEndPoint.x + } else if (newPoints[idx].y === endPoint.y) { + newPoints[idx].y = newEndPoint.y + } + } + + const newPolygon = new QPolygon(newPoints, targetParent.initOptions) + canvas?.add(newPolygon) + canvas?.remove(targetParent) + canvas?.renderAll() + }) + target.on('moving', (e) => { if (target.parentDirection === 'left' || target.parentDirection === 'right') { const minX = target.minX @@ -116,7 +161,6 @@ export function useCanvasEvent() { if (whiteList.includes(e.target.name)) { return } - console.log('removed', e) }, } diff --git a/src/hooks/useMode.js b/src/hooks/useMode.js index ae970461..b6c4680d 100644 --- a/src/hooks/useMode.js +++ b/src/hooks/useMode.js @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { calculateIntersection, distanceBetweenPoints, @@ -25,6 +25,8 @@ import { templateTypeState, wallState, guideLineState, + horiGuideLinesState, + vertGuideLinesState, } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { fabric } from 'fabric' @@ -64,23 +66,22 @@ export function useMode() { const compass = useRecoilValue(compassState) const [isCellCenter, setIsCellCenter] = useState(false) - const guideLineInfo = useRecoilValue(guideLineState) + const [guideLineInfo, setGuideLineInfo] = useRecoilState(guideLineState) const [guideLineMode, setGuideLineMode] = useState(false) const [guideDotMode, setGuideDotMode] = useState(false) + const [horiGuideLines, setHoriGuideLines] = useRecoilState(horiGuideLinesState) + const [vertGuideLines, setVertGuideLines] = useRecoilState(vertGuideLinesState) + useEffect(() => { - // 이벤트 리스너 추가 // if (!canvas) { // canvas?.setZoom(0.8) // return // } - document.addEventListener('keydown', handleKeyDown) + if (!canvas) return + setCanvas(canvas) canvas?.on('mouse:move', drawMouseLines) - // 컴포넌트가 언마운트될 때 이벤트 리스너 제거 - return () => { - document.removeEventListener('keydown', handleKeyDown) - } }, [canvas]) // 빈 배열을 전달하여 컴포넌트가 마운트될 때만 실행되도록 함 useEffect(() => { @@ -100,15 +101,12 @@ export function useMode() { }, [endPoint]) useEffect(() => { + canvas?.off('mouse:out', removeMouseLines) + canvas?.on('mouse:out', removeMouseLines) + changeMode(canvas, mode) canvas?.off('mouse:move') canvas?.on('mouse:move', drawMouseLines) - changeMode(canvas, mode) - /* - if (mode === Mode.EDIT) { - canvas?.off('mouse:down') - canvas?.on('mouse:down', mouseEvent.editMode) - }*/ - }, [mode]) + }, [mode, horiGuideLines, vertGuideLines]) useEffect(() => { setGuideLineMode(false) @@ -142,8 +140,8 @@ export function useMode() { } if (isGuideLineMode) { - horizontalLineArray = [...guideLineState[0].horizontalLineArray] - verticalLineArray = [...guideLineState[0].verticalLineArray] + horizontalLineArray = [...horiGuideLines] + verticalLineArray = [...vertGuideLines] guideLineLengthHori = Number(guideLineState[0].moduleHoriLength) guideLineLengthVert = Number(guideLineState[0].moduleVertLength) } @@ -163,12 +161,27 @@ export function useMode() { if (mode === Mode.EDIT || mode === Mode.ADSORPTION_POINT) { let adsorptionPoint = adsorptionPointList.length > 0 ? findClosestPoint(pointer, adsorptionPointList) : null + if ((horiGuideLines.length > 0 || vertGuideLines.length > 0) && guideDotMode) { + const closestHorizontalLine = getClosestHorizontalLine(pointer, horiGuideLines) + const closetVerticalLine = getClosestVerticalLine(pointer, vertGuideLines) + let intersection = null + let intersectionDistance = Infinity - if (isGuideLineMode && isGuideDotMode) { - 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) + if (closestHorizontalLine && closetVerticalLine) { + intersection = calculateIntersection(closestHorizontalLine, closetVerticalLine) + if (intersection) { + intersectionDistance = distanceBetweenPoints(pointer, intersection) + } + } + + let xDiff, yDiff + + if (closetVerticalLine) { + xDiff = Math.abs(pointer.x - closetVerticalLine.x1) + } + if (closestHorizontalLine) { + yDiff = Math.abs(pointer.y - closestHorizontalLine.y1) + } const x = pointer.x - guideLineLengthHori * Math.floor(pointer.x / guideLineLengthHori) const y = pointer.y - guideLineLengthVert * Math.floor(pointer.y / guideLineLengthVert) @@ -177,22 +190,26 @@ export function useMode() { const yRate = y / guideLineLengthVert const isAttachX = xRate >= 0.4 && xRate <= 0.7 const isAttachY = yRate >= 0.4 && yRate <= 0.7 - if (isAttachX && isAttachY) { newX = Math.floor(pointer.x / guideLineLengthHori) * guideLineLengthHori + guideLineLengthHori / 2 newY = Math.floor(pointer.y / guideLineLengthVert) * guideLineLengthVert + guideLineLengthVert / 2 } else { - if (Math.min(xDiff, yDiff) <= 20) { - if (xDiff < yDiff) { - newX = closetVerticalLine.x1 - newY = pointer.y - } else { - newX = pointer.x - newY = closestHorizontalLine.y1 + if (intersection && intersectionDistance < 20) { + newX = intersection.x + newY = intersection.y + } 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) { + } else if (guideDotMode) { const x = pointer.x - guideLineLengthHori * Math.floor(pointer.x / guideLineLengthHori) const y = pointer.y - guideLineLengthVert * Math.floor(pointer.y / guideLineLengthVert) @@ -205,11 +222,27 @@ export function useMode() { newX = Math.floor(pointer.x / guideLineLengthHori) * guideLineLengthHori + guideLineLengthHori / 2 newY = Math.floor(pointer.y / guideLineLengthVert) * guideLineLengthVert + guideLineLengthVert / 2 } - } else if (isGuideLineMode) { - 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) + } else if (horiGuideLines.length > 0 || vertGuideLines.length > 0) { + const closestHorizontalLine = getClosestHorizontalLine(pointer, horiGuideLines) + const closetVerticalLine = getClosestVerticalLine(pointer, vertGuideLines) + let intersection = null + let intersectionDistance = Infinity + + if (closestHorizontalLine && closetVerticalLine) { + intersection = calculateIntersection(closestHorizontalLine, closetVerticalLine) + if (intersection) { + intersectionDistance = distanceBetweenPoints(pointer, intersection) + } + } + + let xDiff, yDiff + + if (closetVerticalLine) { + xDiff = Math.abs(pointer.x - closetVerticalLine.x1) + } + if (closestHorizontalLine) { + yDiff = Math.abs(pointer.y - closestHorizontalLine.y1) + } const x = pointer.x - guideLineLengthHori * Math.floor(pointer.x / guideLineLengthHori) const y = pointer.y - guideLineLengthVert * Math.floor(pointer.y / guideLineLengthVert) @@ -218,23 +251,26 @@ export function useMode() { const yRate = y / guideLineLengthVert const isAttachX = xRate >= 0.4 && xRate <= 0.7 const isAttachY = yRate >= 0.4 && yRate <= 0.7 - if (isAttachX && isAttachY) { newX = Math.floor(pointer.x / guideLineLengthHori) * guideLineLengthHori + guideLineLengthHori / 2 newY = Math.floor(pointer.y / guideLineLengthVert) * guideLineLengthVert + guideLineLengthVert / 2 } else { - if (Math.min(xDiff, yDiff) <= 20) { - if (xDiff < yDiff) { - newX = closetVerticalLine.x1 - newY = pointer.y - } else { - newX = pointer.x - newY = closestHorizontalLine.y1 + if (intersection && intersectionDistance < 20) { + newX = intersection.x + newY = intersection.y + } else { + if (Math.min(xDiff, yDiff) <= 20) { + if (xDiff < yDiff) { + newX = closetVerticalLine.x1 + newY = pointer.y + } else { + newX = pointer.x + newY = closestHorizontalLine.y1 + } } } } } - if (adsorptionPoint && distanceBetweenPoints(pointer, adsorptionPoint) < 20) { newX = adsorptionPoint.left newY = adsorptionPoint.top @@ -362,16 +398,13 @@ export function useMode() { // 모드에 따른 마우스 이벤트 변경 const changeMouseEvent = (mode) => { - canvas?.off('mouse:down') switch (mode) { case 'drawLine': canvas?.on('mouse:down', mouseEvent.drawLineModeLeftClick) - window.document.removeEventListener('contextmenu', mouseEvent.drawLineModeRightClick) - window.document.addEventListener('contextmenu', mouseEvent.drawLineModeRightClick) + document.addEventListener('contextmenu', mouseEvent.drawLineModeRightClick) break case 'edit': canvas?.on('mouse:down', mouseEvent.editMode) - break case 'textbox': canvas?.on('mouse:down', mouseEvent.textboxMode) @@ -394,9 +427,6 @@ export function useMode() { } } - // 모드에 따른 키보드 이벤트 변경 - const changeKeyboardEvent = (mode) => {} - const keyValid = () => { if (points.current.length === 0) { alert('시작점을 선택해주세요') @@ -552,77 +582,98 @@ export function useMode() { } } - const handleKeyDown = (e) => { - switch (e.key) { - case 'ArrowDown': { - if (!keyValid()) { - return - } - const verticalLength = Number(prompt('길이를 입력하세요:')) - const horizontalLength = 0 + const mouseAndkeyboardEventClear = () => { + canvas?.off('mouse:down') + Object.keys(mouseEvent).forEach((key) => { + canvas?.off('mouse:down', mouseEvent[key]) + document.removeEventListener('contextmenu', mouseEvent[key]) + }) - drawCircleAndLine(verticalLength, horizontalLength) + Object.keys(keyboardEvent).forEach((key) => { + document.removeEventListener('keydown', keyboardEvent[key]) + }) + } - break - } - case 'ArrowUp': { - if (!keyValid()) { - return - } - const verticalLength = -Number(prompt('길이를 입력하세요:')) - const horizontalLength = 0 + const keyboardEvent = { + // rerendering을 막기 위해 useCallback 사용 + editMode: useCallback( + (e) => { + e.preventDefault() + switch (e.key) { + case 'ArrowDown': { + if (!keyValid()) { + return + } + const verticalLength = Number(prompt('길이를 입력하세요:')) + const horizontalLength = 0 - drawCircleAndLine(verticalLength, horizontalLength) + drawCircleAndLine(verticalLength, horizontalLength) - break - } - case 'ArrowLeft': { - if (!keyValid()) { - return - } - const verticalLength = 0 - const horizontalLength = -Number(prompt('길이를 입력하세요:')) - - drawCircleAndLine(verticalLength, horizontalLength) - - break - } - case 'ArrowRight': { - if (!keyValid()) { - return - } - - const verticalLength = 0 - const horizontalLength = Number(prompt('길이를 입력하세요:')) - - drawCircleAndLine(verticalLength, horizontalLength) - - break - } - - case 'Enter': { - const result = prompt('입력하세요 (a(A패턴),b(B패턴),t(지붕))') - - switch (result) { - case 'a': - applyTemplateA() break - case 'b': - applyTemplateB() + } + case 'ArrowUp': { + if (!keyValid()) { + return + } + const verticalLength = -Number(prompt('길이를 입력하세요:')) + const horizontalLength = 0 + + drawCircleAndLine(verticalLength, horizontalLength) + break - case 't': - templateMode() + } + case 'ArrowLeft': { + if (!keyValid()) { + return + } + const verticalLength = 0 + const horizontalLength = -Number(prompt('길이를 입력하세요:')) + + drawCircleAndLine(verticalLength, horizontalLength) + break + } + case 'ArrowRight': { + if (!keyValid()) { + return + } + + const verticalLength = 0 + const horizontalLength = Number(prompt('길이를 입력하세요:')) + + drawCircleAndLine(verticalLength, horizontalLength) + + break + } + + case 'Enter': { + const result = prompt('입력하세요 (a(A패턴),b(B패턴),t(지붕))') + + switch (result) { + case 'a': + applyTemplateA() + break + case 'b': + applyTemplateB() + break + case 't': + templateMode() + break + } + } } - } - } + }, + [canvas], + ), } const changeMode = (canvas, mode) => { + mouseAndkeyboardEventClear() setMode(mode) setCanvas(canvas) // mode별 이벤트 변경 + changeMouseEvent(mode) changeKeyboardEvent(mode) @@ -657,40 +708,82 @@ export function useMode() { } } + const changeKeyboardEvent = (mode) => { + if (mode === Mode.EDIT) { + switch (mode) { + case 'edit': + document.addEventListener('keydown', keyboardEvent.editMode) + break + } + } + } + const mouseEvent = { drawLineModeLeftClick: (options) => { + if (mode !== Mode.DRAW_LINE) { + return + } const pointer = canvas?.getPointer(options.e) const line = new QLine( - [pointer.x, 0, pointer.x, canvas.height], // y축에 1자 선을 그립니다. + [pointer.x, 0, pointer.x, canvasSize.vertical], // y축에 1자 선을 그립니다. { - stroke: 'black', - strokeWidth: 2, - viewLengthText: true, - selectable: false, - fontSize: fontSize, + stroke: 'gray', + strokeWidth: 1, + selectable: true, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + name: 'guideLine', + direction: 'vertical', }, ) canvas?.add(line) canvas?.renderAll() - }, - drawLineModeRightClick: (options) => { - const line = new fabric.Line( - [0, options.offsetY, canvas.width, options.offsetY], // y축에 1자 선을 그립니다. - { - stroke: 'black', - strokeWidth: 2, - viewLengthText: true, - selectable: false, - fontSize: fontSize, - }, - ) - canvas?.add(line) - canvas?.renderAll() + const newVerticalLineArray = [...vertGuideLines] + newVerticalLineArray.push(line) + + setVertGuideLines(newVerticalLineArray) }, + drawLineModeRightClick: useCallback( + (options) => { + document.removeEventListener('contextmenu', mouseEvent.drawLineModeRightClick) + if (mode !== Mode.DRAW_LINE) { + return + } + const line = new fabric.Line( + [0, options.offsetY, canvasSize.horizontal, options.offsetY], // y축에 1자 선을 그립니다. + { + stroke: 'gray', + strokeWidth: 1, + selectable: true, + lockMovementX: true, + lockMovementY: true, + lockRotation: true, + lockScalingX: true, + lockScalingY: true, + name: 'guideLine', + direction: 'horizontal', + }, + ) + + canvas?.add(line) + canvas?.renderAll() + + const newHorizontalLineArray = [...horiGuideLines] + newHorizontalLineArray.push(line) + setHoriGuideLines(newHorizontalLineArray) + }, + [canvas, mode, horiGuideLines], + ), editMode: (options) => { + if (mode !== Mode.EDIT) { + return + } let pointer = canvas?.getPointer(options.e) if (getInterSectPointByMouseLine()) { @@ -801,7 +894,9 @@ export function useMode() { canvas?.renderAll() }, + textboxMode: (options) => { + if (mode !== Mode.TEXTBOX) return if (canvas?.getActiveObject()?.type === 'textbox') return const pointer = canvas?.getPointer(options.e) @@ -821,6 +916,7 @@ export function useMode() { }) }, drawRectMode: (o) => { + if (mode !== Mode.DRAW_RECT) return let rect, isDown, origX, origY isDown = true const pointer = canvas.getPointer(o.e) @@ -862,7 +958,8 @@ export function useMode() { }) }, // 흡착점 추가 - adsorptionPoint(o) { + adsorptionPoint: (o) => { + if (mode !== Mode.ADSORPTION_POINT) return const pointer = canvas.getPointer(o.e) let newX = pointer.x let newY = pointer.y @@ -4484,7 +4581,6 @@ export function useMode() { canvas?.off('mouse:move') canvas?.off('mouse:out') - document.removeEventListener('keydown', handleKeyDown) const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roof') roofs.forEach((roof, index) => { const offsetPolygonPoint = offsetPolygon(roof.points, -20) diff --git a/src/store/canvasAtom.js b/src/store/canvasAtom.js index c2b245d7..8d16b98f 100644 --- a/src/store/canvasAtom.js +++ b/src/store/canvasAtom.js @@ -95,3 +95,15 @@ export const currentObjectState = atom({ default: null, dangerouslyAllowMutability: true, }) + +export const horiGuideLinesState = atom({ + key: 'horiGuideLines', + default: [], + dangerouslyAllowMutability: true, +}) + +export const vertGuideLinesState = atom({ + key: 'vertGuideLines', + default: [], + dangerouslyAllowMutability: true, +}) diff --git a/src/styles/_test.scss b/src/styles/_test.scss index babec92c..55440362 100644 --- a/src/styles/_test.scss +++ b/src/styles/_test.scss @@ -1,3 +1,81 @@ .test { background-color: #121212; } + +.grid-container2 { + display: grid; + grid-template-columns: repeat(2, 1fr); /* 2개의 열 */ + grid-template-rows: repeat(6, 30px); /* 6개의 행 */ + justify-items: center; /* 각 그리드 아이템을 수평 가운데 정렬 */ + align-items: center; /* 각 그리드 아이템을 수직 가운데 정렬 */ + gap: 5px; /* 그리드 아이템 사이의 간격 */ +} + +.grid-container3 { + display: grid; + grid-template-columns: repeat(3, 1fr); /* 3개의 열 */ + grid-template-rows: repeat(6, 30px); /* 6개의 행 */ + justify-items: center; /* 각 그리드 아이템을 수평 가운데 정렬 */ + align-items: center; /* 각 그리드 아이템을 수직 가운데 정렬 */ + gap: 5px; /* 그리드 아이템 사이의 간격 */ +} + +.grid-container4 { + display: grid; + grid-template-columns: repeat(4, 1fr); /* 4개의 열 */ + grid-template-rows: repeat(6, 30px); /* 6개의 행 */ + justify-items: center; /* 각 그리드 아이템을 수평 가운데 정렬 */ + align-items: center; /* 각 그리드 아이템을 수직 가운데 정렬 */ + gap: 5px; /* 그리드 아이템 사이의 간격 */ +} + +.grid-container5 { + display: grid; + grid-template-columns: repeat(5, 1fr); /* 5개의 열 */ + grid-template-rows: repeat(5, 30px); /* 5개의 행 */ + justify-items: center; /* 각 그리드 아이템을 수평 가운데 정렬 */ + align-items: center; /* 각 그리드 아이템을 수직 가운데 정렬 */ + gap: 0px; /* 그리드 아이템 사이의 간격 */ +} + +.grid-item { + width: 100%; + height: 100%; + border: 1px solid black; /* 그리드 외각선 */ + text-align: center; /* 그리드 내 가운데 정렬 */ +} + +.grid-item2 { + padding: 20px; + text-align: center; + cursor: pointer; + border: 1px solid #000; +} + +.grid-item3 { + padding: 20px; + text-align: center; + cursor: pointer; + border: 1px solid #000; + transition: background-color 0.3s ease; +} + +.grid-item.Y { + background-color: #d3d0d0; + color: black; +} + +.grid-item.N { + background-color: white; + color: black; +} + +.grid-item.selected { + background-color: #d3d0d0; + color: black; +} + +.grid-item.unselected { + background-color: white; + color: black; +} diff --git a/src/util/common-utils.js b/src/util/common-utils.js index 9f53fede..04f3dccf 100644 --- a/src/util/common-utils.js +++ b/src/util/common-utils.js @@ -9,3 +9,45 @@ export const isObjectNotEmpty = (obj) => { } return Object.keys(obj).length > 0 } + +/** + * ex) const params = {page:10, searchDvsnCd: 20} + * @param {*} params + * @returns page=10&searchDvsnCd=20 + */ +export const queryStringFormatter = (params = {}) => { + const queries = [] + Object.keys(params).forEach((parameterKey) => { + const parameterValue = params[parameterKey] + + if (parameterValue === undefined || parameterValue === null) { + return + } + + // string trim + if (typeof parameterValue === 'string' && !parameterValue.trim()) { + return + } + + // array to query string + if (Array.isArray(parameterValue)) { + // primitive type + if (parameterValue.every((v) => typeof v === 'number' || typeof v === 'string')) { + queries.push(`${encodeURIComponent(parameterKey)}=${parameterValue.map((v) => encodeURIComponent(v)).join(',')}`) + return + } + // reference type + if (parameterValue.every((v) => typeof v === 'object' && v !== null)) { + parameterValue.map((pv, i) => { + return Object.keys(pv).forEach((valueKey) => { + queries.push(`${encodeURIComponent(`${parameterKey}[${i}].${valueKey}`)}=${encodeURIComponent(pv[valueKey])}`) + }) + }) + return + } + } + // 나머지 + queries.push(`${encodeURIComponent(parameterKey)}=${encodeURIComponent(parameterValue)}`) + }) + return queries.join('&') +} diff --git a/yarn.lock b/yarn.lock index c2324b6c..ee262584 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4319,6 +4319,11 @@ date-fns@^3.3.1: resolved "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz" integrity sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww== +dayjs@^1.11.13: + version "1.11.13" + resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.13.tgz#92430b0139055c3ebb60150aa13e860a4b5a366c" + integrity sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg== + debug@4, debug@^4.3.3, debug@^4.3.4: version "4.3.5" resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz"