diff --git a/.env.development b/.env.development index 9a1529ed..f8aa1483 100644 --- a/.env.development +++ b/.env.development @@ -1,8 +1,10 @@ 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" -SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" \ No newline at end of file +SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" + +NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" \ No newline at end of file diff --git a/.env.production b/.env.production index 8970b77b..91d91ce1 100644 --- a/.env.production +++ b/.env.production @@ -4,4 +4,6 @@ NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080" DATABASE_URL="" -SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" \ No newline at end of file +SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" + +NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" \ No newline at end of file diff --git a/package.json b/package.json index bbd8c881..9f060d6d 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "react-colorful": "^5.6.1", "react-datepicker": "^7.3.0", "react-dom": "^18", + "react-icons": "^5.3.0", "react-responsive-modal": "^6.4.2", "react-toastify": "^10.0.5", "recoil": "^0.7.7", @@ -32,6 +33,7 @@ }, "devDependencies": { "@turf/turf": "^7.0.0", + "convertapi": "^1.14.0", "dayjs": "^1.11.13", "postcss": "^8", "prettier": "^3.3.3", diff --git a/public/drawTemplates/153302.svg b/public/drawTemplates/153302.svg deleted file mode 100644 index 3b1c97b0..00000000 --- a/public/drawTemplates/153302.svg +++ /dev/null @@ -1,23 +0,0 @@ - - - - -Created by potrace 1.15, written by Peter Selinger 2001-2017 - - - - - - - diff --git a/public/drawTemplates/shape21.svg b/public/drawTemplates/shape21.svg deleted file mode 100644 index a2d901e7..00000000 --- a/public/drawTemplates/shape21.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/app/[locale]/floor-plan/page.jsx b/src/app/[locale]/floor-plan/page.jsx new file mode 100644 index 00000000..7ca0fc3e --- /dev/null +++ b/src/app/[locale]/floor-plan/page.jsx @@ -0,0 +1,5 @@ +import FloorPlan from '@/components/floor-plan/FloorPlan' + +export default function floorPlanPage() { + return +} diff --git a/src/app/api/html2canvas/route.js b/src/app/api/html2canvas/route.js new file mode 100644 index 00000000..54af611d --- /dev/null +++ b/src/app/api/html2canvas/route.js @@ -0,0 +1,29 @@ +'use server' + +import fs from 'fs/promises' + +import { NextResponse } from 'next/server' + +export async function GET(req) { + const path = 'public/mapImages' + const q = req.nextUrl.searchParams.get('q') + const fileNm = req.nextUrl.searchParams.get('fileNm') + const zoom = req.nextUrl.searchParams.get('zoom') + const targetUrl = `https://maps.googleapis.com/maps/api/staticmap?center=${q}&zoom=${zoom}&maptype=satellite&size=640x640&scale=1&key=AIzaSyDO7nVR1N_D2tKy60hgGFavpLaXkHpiHpc` + const decodeUrl = decodeURIComponent(targetUrl) + + const response = await fetch(decodeUrl) + + const data = await response.arrayBuffer() + const buffer = Buffer.from(data) + + try { + await fs.readdir(path) + } catch { + await fs.mkdir(path) + } finally { + await fs.writeFile(`${path}/${fileNm}.png`, buffer) + } + + return NextResponse.json({ fileNm: `${fileNm}.png` }) +} diff --git a/src/common/common.js b/src/common/common.js index 91a9583a..b1cca49e 100644 --- a/src/common/common.js +++ b/src/common/common.js @@ -12,6 +12,8 @@ export const Mode = { CELL_POWERCON: 'cellPowercon', //파워콘 DRAW_HELP_LINE: 'drawHelpLine', // 보조선 그리기 모드 지붕 존재해야함 ADSORPTION_POINT: 'adsorptionPoint', //흡착점 모드 + OPENING: 'opening', //개구 모드 + SHADOW: 'shadow', //그림자 생성 모드 DEFAULT: 'default', } diff --git a/src/components/Intro.jsx b/src/components/Intro.jsx index 0129e7a4..840aff7a 100644 --- a/src/components/Intro.jsx +++ b/src/components/Intro.jsx @@ -14,7 +14,7 @@ import { Button } from '@nextui-org/react' import SingleDatePicker from './common/datepicker/SingleDatePicker' import RangeDatePicker from './common/datepicker/RangeDatePicker' import QGrid from './common/grid/QGrid' -import { QToast } from '@/hooks/useToast' +import { toastUp } from '@/hooks/useToast' export default function Intro() { const { get } = useAxios() @@ -127,7 +127,7 @@ export default function Intro() { + + +
+

구글 맵 이미지 사용

+ +
+ +
+ {useGoogleMapFile && ( + <> +
+

Zoom Controller : {zoom}

+ + +
+
+ +
+ + )} +
) diff --git a/src/components/Roof2.jsx b/src/components/Roof2.jsx index ff570d90..c8a48868 100644 --- a/src/components/Roof2.jsx +++ b/src/components/Roof2.jsx @@ -1,44 +1,62 @@ 'use client' import { useCanvas } from '@/hooks/useCanvas' -import { useCallback, useEffect, useRef, useState } from 'react' +import { useEffect, useRef, useState } from 'react' import { v4 as uuidv4 } from 'uuid' import { useMode } from '@/hooks/useMode' import { Mode } from '@/common/common' -import { Button } from '@nextui-org/react' +import { Button, Input } from '@nextui-org/react' import RangeSlider from './ui/RangeSlider' import { useRecoilState, useRecoilValue } from 'recoil' import { + cadFileCompleteState, + cadFileNameState, canvasSizeState, compassState, currentObjectState, fontSizeState, + globalCompassState, + googleMapFileNameState, roofMaterialState, roofState, sortedPolygonArray, templateTypeState, + useCadFileState, + useGoogleMapFileState, wallState, } from '@/store/canvasAtom' import { QLine } from '@/components/fabric/QLine' import { getCanvasState, insertCanvasState } from '@/lib/canvas' -import { calculateIntersection, distanceBetweenPoints } from '@/util/canvas-util' +import { calculateIntersection } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' -import ThumbnailList from './ui/ThumbnailLIst' import QContextMenu from './common/context-menu/QContextMenu' import { modalContent, modalState } from '@/store/modalAtom' 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' import { SurfaceShapeModal } from '@/components/ui/SurfaceShape' +import { drawDirectionStringToArrow } from '@/util/qpolygon-utils' +import ThumbnailList from '@/components/ui/ThumbnailLIst' +import ObjectPlacement from '@/components/ui/ObjectPlacement' export default function Roof2(props) { const { name, userId, email, isLoggedIn } = props - const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas') + const { + canvas, + handleRedo, + handleUndo, + setCanvasBackgroundWithDots, + saveImage, + addCanvas, + handleBackImageLoadToCanvas, + handleCadImageInit, + backImg, + setBackImg, + } = useCanvas('canvas') const { get } = useAxios() @@ -84,6 +102,20 @@ export default function Roof2(props) { canvas, } + let imgPath + // cad 파일 업로드 + const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState) + const [cadFileName, setCadFileName] = useRecoilState(cadFileNameState) + const [cadFileComplete, setCadFileComplete] = useRecoilState(cadFileCompleteState) + useCadFile && (imgPath = `/cadImages/${cadFileName}`) + + // 구글맵 이미지 업로드 + const [useGoogleMapFile, setUseGoogleMapFile] = useRecoilState(useGoogleMapFileState) + const [googleMapFileName, setGoogleMapFileName] = useRecoilState(googleMapFileNameState) + useGoogleMapFile && (imgPath = `/mapImages/${googleMapFileName}`) + + const [globalCampass, setGlobalCampass] = useRecoilState(globalCompassState) + const { mode, setMode, @@ -130,6 +162,15 @@ export default function Roof2(props) { return } changeMode(canvas, mode) + + if (!cadFileComplete && useCadFile) { + // cad 파일 로드 + useCadFile && handleBackImageLoadToCanvas(imgPath, canvas) + } + + if (useGoogleMapFile) { + handleBackImageLoadToCanvas(imgPath, canvas) + } }, [canvas, mode]) const makeLine = () => { @@ -535,44 +576,6 @@ export default function Roof2(props) { }) } - const setCurrentPattern = (polygon) => { - const { width, height, roofStyle } = roofMaterial - const roofRatio = window.devicePixelRatio || 1 - const patternSourceCanvas = document.createElement('canvas') - - if (roofStyle === 1) { - patternSourceCanvas.width = width * roofRatio - patternSourceCanvas.height = height * roofRatio - } else if (roofStyle === 2) { - patternSourceCanvas.width = width * 2 - patternSourceCanvas.height = height * 2 - } - - const ctx = patternSourceCanvas.getContext('2d') - - ctx.scale(roofRatio, roofRatio) - ctx.strokeStyle = 'green' - ctx.lineWidth = 0.4 - // 벽돌 패턴 그리기 - if (roofStyle === 1) { - ctx.strokeRect(0, 0, 50, 30) - } else if (roofStyle === 2) { - // 지그재그 - ctx.strokeRect(0, 0, 200, 100) - ctx.strokeRect(100, 100, 200, 100) - } - - // 패턴 생성 - const pattern = new fabric.Pattern({ - source: patternSourceCanvas, - repeat: 'repeat', - }) - polygon.set('fill', null) - - polygon.set('fill', pattern) - canvas?.renderAll() - } - /** * canvas 내용 불러오기 */ @@ -606,944 +609,29 @@ export default function Roof2(props) { } const changeLength = (e) => { - setScale(e) const polygon = canvas?.getActiveObject() - if (polygon.type !== 'QPolygon') { + + if (polygon?.type !== 'QPolygon') { return } + setScale(e) + polygon.setScaleX(e) canvas?.renderAll() } - 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 moduleConfiguration = () => { + createRoofRack() } - 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 setDirectionStringToArrow = () => { + drawDirectionStringToArrow(canvas, globalCampass) } - 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 - }) - } - const createTemplate19 = () => { - const length1 = 500 //Number(prompt('1번')) - const length2 = 300 //Number(prompt('2번')) - const length3 = 400 //Number(prompt('3번')) - const length4 = 300 //Number(prompt('4번')) - - // 좌측 빗변 - const leftHypotenuse = Math.sqrt(((length1 - length2) / 2) ** 2 + length3 ** 2) - const rightHypotenuse = (length4 / length3) * leftHypotenuse - - const leftAngle = Math.acos((length1 - length2) / 2 / leftHypotenuse) - - 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 + leftHypotenuse * Math.cos(leftAngle), y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(leftAngle) }, - { 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 - rightHypotenuse * Math.cos(leftAngle), y: pointer.y + length3 / 2 - rightHypotenuse * Math.sin(leftAngle) }, - { - x: pointer.x + length1 / 2 - rightHypotenuse * Math.cos(leftAngle) - length2, - y: pointer.y + length3 / 2 - rightHypotenuse * Math.sin(leftAngle), - }, - ], - { - fill: 'transparent', - stroke: 'black', - strokeWidth: 2, - selectable: true, - fontSize: fontSize, - name: 'guideTriangle', - }, - ) - - canvas?.add(triangle) - }) - - canvas?.on('mouse:down', (e) => { - isDrawing = false - }) - } - const createTemplate20 = () => { - const length1 = 500 //Number(prompt('1번')) - const length2 = 300 //Number(prompt('2번')) - const length3 = 400 //Number(prompt('3번')) - const length4 = 300 //Number(prompt('4번')) - - // 좌측 빗변 - const rightHypotenuse = Math.sqrt(((length1 - length2) / 2) ** 2 + length3 ** 2) - const leftHypotenuse = (length4 / length3) * rightHypotenuse - - const rightAngle = Math.acos((length1 - length2) / 2 / rightHypotenuse) - - 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 - rightHypotenuse * Math.cos(rightAngle), - y: pointer.y + length3 / 2 - rightHypotenuse * Math.sin(rightAngle), - }, - { 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 + leftHypotenuse * Math.cos(rightAngle), y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(rightAngle) }, - { - x: pointer.x - length1 / 2 + leftHypotenuse * Math.cos(rightAngle) + length2, - y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(rightAngle), - }, - ], - { - 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 && ( <>
- - {/* - */} + { + + } @@ -1676,6 +765,15 @@ export default function Roof2(props) { > 면형상 + {/**/} @@ -1710,62 +808,42 @@ export default function Roof2(props) { - - - - - - - - - - - - - - - - - - - +
+
+ {/* Compass Circle */} +
+ {/* N, S, E, W Labels */} +
N
+
S
+
+ E +
+
+ W +
+
+ + {/* Compass Pointer */} +
+ {/* Red Upper Triangle */} +
+
+
{!canvas ? null : mode === Mode.DRAW_LINE ? ( diff --git a/src/components/common/context-menu/QContextMenu.jsx b/src/components/common/context-menu/QContextMenu.jsx index 57aaf45b..4658ef6f 100644 --- a/src/components/common/context-menu/QContextMenu.jsx +++ b/src/components/common/context-menu/QContextMenu.jsx @@ -1,6 +1,5 @@ 'use client' -import { useEffect, useState } from 'react' -import { useRecoilState, useRecoilValue } from 'recoil' +import { Children, useEffect, useState } from 'react' export default function QContextMenu(props) { const { contextRef, canvasProps } = props @@ -8,6 +7,19 @@ export default function QContextMenu(props) { // const children = useRecoilValue(modalContent) const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 }) + const activeObject = canvasProps.getActiveObject() //액티브된 객체를 가져옴 + + let contextType = '' + + if (activeObject) { + if (activeObject.initOptions) { + //이건 바뀔 가능성이 있음 + if (activeObject.initOptions.name.indexOf('guide') > -1) { + contextType = 'surface' //면형상 + } + } + } + useEffect(() => { if (!contextRef.current) return @@ -29,21 +41,49 @@ export default function QContextMenu(props) { } } - // Prevent the default context menu from appearing on the canvas canvasProps.upperCanvasEl.addEventListener('contextmenu', handleContextMenu) document.addEventListener('click', handleClick) document.addEventListener('click', handleOutsideClick) return () => { - // canvasProps.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) document.removeEventListener('click', handleClick) document.removeEventListener('click', handleOutsideClick) } }, [contextRef, contextMenu]) - const handleMenuClick = (option) => { - alert(`option ${option} clicked`) - setContextMenu({ ...contextMenu, visible: false }) + const handleObjectMove = () => { + activeObject.set({ + lockMovementX: false, // X 축 이동 잠금 + lockMovementY: false, // Y 축 이동 잠금 + }) + + canvasProps.on('object:modified', function (e) { + activeObject.set({ + lockMovementX: true, // X 축 이동 잠금 + lockMovementY: true, // Y 축 이동 잠금 + }) + }) + } + + const handleObjectDelete = () => { + if (confirm('삭제하실거?')) { + canvasProps.remove(activeObject) + } + } + + const handleObjectCopy = () => { + activeObject.clone((cloned) => { + cloned.set({ + left: activeObject.left + activeObject.width + 20, + initOptions: { ...activeObject.initOptions }, + lockMovementX: true, // X 축 이동 잠금 + lockMovementY: true, // Y 축 이동 잠금 + lockRotation: true, // 회전 잠금 + lockScalingX: true, // X 축 크기 조정 잠금 + lockScalingY: true, // Y 축 크기 조정 잠금 + }) + canvasProps?.add(cloned) + }) } return ( @@ -51,15 +91,16 @@ export default function QContextMenu(props) { {contextMenu.visible && (
    -
  • handleMenuClick(1)}> - Option 1 +
  • handleObjectMove()}> + 이동
  • -
  • handleMenuClick(2)}> - Option 2 +
  • handleObjectDelete()}> + 삭제
  • -
  • handleMenuClick(3)}> - Option 3 +
  • handleObjectCopy()}> + 복사
  • + {props.children}
)} diff --git a/src/components/common/context-menu/QPolygonContextMenu.jsx b/src/components/common/context-menu/QPolygonContextMenu.jsx index b3246165..12f8e20b 100644 --- a/src/components/common/context-menu/QPolygonContextMenu.jsx +++ b/src/components/common/context-menu/QPolygonContextMenu.jsx @@ -1,71 +1,20 @@ 'use client' -import { useEffect, useState } from 'react' -import { useRecoilState, useRecoilValue } from 'recoil' +import QContextMenu from './QContextMenu' export default function QPolygonContextMenu(props) { const { contextRef, canvasProps } = props - // const children = useRecoilValue(modalContent) - const [contextMenu, setContextMenu] = useState({ visible: false, x: 0, y: 0 }) - - useEffect(() => { - if (!contextRef.current) return - - const handleContextMenu = (e) => { - e.preventDefault() //기존 contextmenu 막고 - setContextMenu({ visible: true, x: e.pageX, y: e.pageY }) - canvasProps.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) //한번 노출 후 이벤트 삭제 - } - - const handleClick = (e) => { - e.preventDefault() - setContextMenu({ ...contextMenu, visible: false }) - } - - const handleOutsideClick = (e) => { - e.preventDefault() - if (contextMenu.visible && !ref.current.contains(e.target)) { - setContextMenu({ ...contextMenu, visible: false }) - } - } - - // Prevent the default context menu from appearing on the canvas - canvasProps.upperCanvasEl.addEventListener('contextmenu', handleContextMenu) - document.addEventListener('click', handleClick) - document.addEventListener('click', handleOutsideClick) - - return () => { - // canvasProps.upperCanvasEl.removeEventListener('contextmenu', handleContextMenu) - document.removeEventListener('click', handleClick) - document.removeEventListener('click', handleOutsideClick) - } - }, [contextRef, contextMenu]) - - const handleMenuClick = (option) => { - alert(`option ${option} clicked`) - setContextMenu({ ...contextMenu, visible: false }) + function handleMenuClick(index) { + alert('test') } return ( - <> - {contextMenu.visible && ( -
-
    -
  • handleMenuClick(1)}> - polygon -
  • -
  • handleMenuClick(1)}> - Option 1 -
  • -
  • handleMenuClick(2)}> - Option 2 -
  • -
  • handleMenuClick(3)}> - Option 3 -
  • -
-
- )} - + + <> +
  • handleMenuClick(4)}> + 모듈 채우기 +
  • + +
    ) } diff --git a/src/components/fabric/QPolygon.js b/src/components/fabric/QPolygon.js index 355fdf7f..3c8b2148 100644 --- a/src/components/fabric/QPolygon.js +++ b/src/components/fabric/QPolygon.js @@ -2,7 +2,15 @@ import { fabric } from 'fabric' import { v4 as uuidv4 } from 'uuid' import { QLine } from '@/components/fabric/QLine' import { distanceBetweenPoints, findTopTwoIndexesByDistance, getDirectionByPoint, sortedPointLessEightPoint, sortedPoints } from '@/util/canvas-util' -import { calculateAngle, drawHippedRoof, inPolygon, splitPolygonWithLines, toGeoJSON } from '@/util/qpolygon-utils' +import { + calculateAngle, + drawDirectionArrow, + drawHippedRoof, + drawPolygonArrow, + inPolygon, + splitPolygonWithLines, + toGeoJSON, +} from '@/util/qpolygon-utils' import * as turf from '@turf/turf' export const QPolygon = fabric.util.createClass(fabric.Polygon, { @@ -19,6 +27,8 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { innerLines: [], children: [], initOptions: null, + direction: null, + arrow: null, initialize: function (points, options, canvas) { // 소수점 전부 제거 points.forEach((point) => { @@ -100,6 +110,7 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { toObject: function (propertiesToInclude) { return fabric.util.object.extend(this.callSuper('toObject', propertiesToInclude), { + id: this.id, type: this.type, text: this.text, hips: this.hips, @@ -116,9 +127,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.on('modified', (e) => { this.addLengthText() + if (this.arrow) { + drawDirectionArrow(this) + } }) this.on('selected', () => { + drawDirectionArrow(this) Object.keys(this.controls).forEach((controlKey) => { if (controlKey !== 'ml' && controlKey !== 'mr') { this.setControlVisible(controlKey, false) @@ -132,6 +147,17 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.canvas.remove(text) }) this.texts = null + + if (this.arrow) { + this.canvas.remove(this.arrow) + this.canvas + .getObjects() + .filter((obj) => obj.name === 'directionText' && obj.parent === this.arrow) + .forEach((text) => { + this.canvas.remove(text) + }) + this.arrow = null + } }) // polygon.fillCell({ width: 50, height: 30, padding: 10 }) @@ -162,11 +188,13 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { }, addLengthText() { - if (this.texts.length > 0) { - this.texts.forEach((text) => { + this.canvas + ?.getObjects() + .filter((obj) => obj.name === 'lengthText' && obj.parent === this) + .forEach((text) => { this.canvas.remove(text) }) - } + let points = this.getCurrentPoints() points.forEach((start, i) => { @@ -209,7 +237,12 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { }, setFontSize(fontSize) { this.fontSize = fontSize - this.text.set({ fontSize }) + this.canvas + ?.getObjects() + .filter((obj) => obj.name === 'lengthText' && obj.parent === this) + .forEach((text) => { + text.set({ fontSize: fontSize }) + }) }, _render: function (ctx) { this.callSuper('_render', ctx) @@ -689,9 +722,20 @@ export const QPolygon = fabric.util.createClass(fabric.Polygon, { this.wall = wall }, setViewLengthText(isView) { - this.texts.forEach((text) => { - text.set({ visible: isView }) - }) + this.canvas + ?.getObjects() + .filter((obj) => obj.name === 'lengthText' && obj.parent === this) + .forEach((text) => { + text.set({ visible: isView }) + }) + }, + setScaleX(scale) { + this.scaleX = scale + this.addLengthText() + }, + setScaleY(scale) { + this.scaleY = scale + this.addLengthText() }, divideLine() { splitPolygonWithLines(this) diff --git a/src/components/floor-plan/FloorPlan.jsx b/src/components/floor-plan/FloorPlan.jsx new file mode 100644 index 00000000..27e03cf6 --- /dev/null +++ b/src/components/floor-plan/FloorPlan.jsx @@ -0,0 +1,7 @@ +export default function FloorPlan() { + return ( + <> +

    도면 작성 페이지

    + + ) +} diff --git a/src/components/ui/ObjectPlacement.jsx b/src/components/ui/ObjectPlacement.jsx new file mode 100644 index 00000000..7bb10f14 --- /dev/null +++ b/src/components/ui/ObjectPlacement.jsx @@ -0,0 +1,155 @@ +import React, { useCallback, useEffect, useRef, useState } from 'react' +import { Button, Input } from '@nextui-org/react' +import { useRecoilState, useSetRecoilState } from 'recoil' +import { modalState } from '@/store/modalAtom' +import { fabric } from 'fabric' +import { QPolygon } from '@/components/fabric/QPolygon' +import { modeState, objectPlacementModeState } from '@/store/canvasAtom' + +const BATCH_TYPE = { + OPENING: 'opening', + SHADOW: 'shadow', +} + +const INPUT_TYPE = { + FREE: 'free', + DIMENSION: 'dimension', +} + +const ObjectPlacement = ({ canvas }) => { + const [open, setOpen] = useRecoilState(modalState) + const [mode, setMode] = useRecoilState(modeState) + const [objectPlacementMode, setObjectPlacementModeState] = useRecoilState(objectPlacementModeState) + const [width, setWidth] = useState(0) + const [height, setHeight] = useState(0) + const [areaBoundary, setAreaBoundary] = useState(true) + + // opening or shadow 개구 / 그림자 + const [batchType, setBatchType] = useState(BATCH_TYPE.OPENING) + + // free or dimension 프리 / 치수 + const [inputType, setInputType] = useState(INPUT_TYPE.FREE) + + const handleSave = () => { + setMode(batchType) + setOpen(false) + } + + return ( +
    +
    + +
    + +
    +

    개구 · 그림자 배치

    +
    + +
    +
    + + +
    +
    + +
    +
    설정
    + +
    + +
    + +
    + +
    + +
    +
    + + { + setObjectPlacementModeState({ ...objectPlacementMode, width: e.target.value }) + }} + /> +
    + +
    + + { + setObjectPlacementModeState({ ...objectPlacementMode, height: e.target.value }) + }} + /> +
    +
    + +
    + +
    + +
    + +
    +
    +
    + ) +} + +export default ObjectPlacement diff --git a/src/components/ui/SurfaceShape.jsx b/src/components/ui/SurfaceShape.jsx index b55db429..ac30138f 100644 --- a/src/components/ui/SurfaceShape.jsx +++ b/src/components/ui/SurfaceShape.jsx @@ -1,9 +1,11 @@ import { Button, Input } from '@nextui-org/react' -import { useCallback, useEffect, useState } from 'react' -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil' +import { useState } from 'react' +import { useRecoilValue, useSetRecoilState } from 'recoil' import { modalState } from '@/store/modalAtom' import { QPolygon } from '@/components/fabric/QPolygon' -import { fontSizeState } from '@/store/canvasAtom' +import { fontSizeState, roofMaterialState } from '@/store/canvasAtom' +import { degreesToRadians } from '@turf/turf' +import { getIntersectionPoint } from '@/util/canvas-util' /** * 면형상 배치 모달 @@ -14,6 +16,8 @@ export const SurfaceShapeModal = ({ canvas }) => { const [type, setType] = useState(0) const setOpen = useSetRecoilState(modalState) const fontSize = useRecoilValue(fontSizeState) + //지붕재 + const roofMaterial = useRecoilValue(roofMaterialState) /** * 최대 5개의 length @@ -24,6 +28,9 @@ export const SurfaceShapeModal = ({ canvas }) => { const [length4, setLength4] = useState(0) const [length5, setLength5] = useState(0) + // 방향 + const [direction, setDirection] = useState('south') + /** * 최대 5개의 length */ @@ -36,203 +43,585 @@ export const SurfaceShapeModal = ({ canvas }) => { } const onSave = () => { + if (!checkValid()) { + alert('안됨') + return + } let isDrawing = true let obj = null + let points = [] canvas?.on('mouse:move', (e) => { if (!isDrawing) { return } + const pointer = canvas?.getPointer(e.e) + canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) switch (type) { case 1: { - canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) - const pointer = canvas?.getPointer(e.e) - obj = 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', - }, - ) + let newLength2 = length2 + + if (length3 !== 0) { + newLength2 = Math.sqrt(length3 ** 2 - (length1 / 2) ** 2) + } + + points = [ + { x: pointer.x, y: pointer.y - parseInt(newLength2) / 2 }, + { x: pointer.x - parseInt(length1) / 2, y: pointer.y + parseInt(newLength2) / 2 }, + { x: pointer.x + parseInt(length1) / 2, y: pointer.y + parseInt(newLength2) / 2 }, + ] break } case 2: { - canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) - const pointer = canvas?.getPointer(e.e) - obj = 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', - }, - ) + points = [ + { 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 }, + ] + break } case 3: { - canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) - const pointer = canvas?.getPointer(e.e) - obj = 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', - }, - ) + points = [ + { 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 }, + ] + break } case 4: { - canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) - const pointer = canvas?.getPointer(e.e) - obj = 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', - }, - ) + points = [ + { 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 }, + ] break } case 5: { - canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) - const pointer = canvas?.getPointer(e.e) - obj = 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', - }, - ) + points = [ + { 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 }, + ] + break } case 6: { - canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) - const pointer = canvas?.getPointer(e.e) - obj = 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', - }, - ) + points = [ + { 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 }, + ] + break } case 7: { - canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) - const pointer = canvas?.getPointer(e.e) - obj = 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', - }, - ) + points = [ + { 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 }, + ] + + break } case 8: { const angleInRadians = Math.asin(length2 / length3) - canvas?.remove(...canvas?.getObjects().filter((obj) => obj.name === 'guideTriangle')) - const pointer = canvas?.getPointer(e.e) - obj = 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', - }, - ) + points = [ + { 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 }, + ] + + break } case 9: { const angleInRadians = Math.asin(length2 / length3) - const pointer = canvas?.getPointer(e.e) - obj = new QPolygon( + points = [ + { 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 }, + ] + + break + } + case 10: { + points = [ + { 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), + }, + ] + + break + } + case 11: { + points = [ + { 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) }, + ] + + break + } + case 12: { + points = [ + { 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) }, + ] + + break + } + case 13: { + points = [ + { 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) }, + ] + break + } + case 14: { + points = [ + { 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) }, + ] + break + } + case 15: { + points = [ + { 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) }, + ] + break + } + case 16: { + points = [ + { 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) }, + ] + break + } + case 17: { + points = [ + { 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) }, + ] + break + } + case 18: { + points = [ + { 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) }, + ] + break + } + + case 19: { + const leftHypotenuse = Math.sqrt(((length1 - length2) / 2) ** 2 + length3 ** 2) + const rightHypotenuse = (length4 / length3) * leftHypotenuse + + const leftAngle = Math.acos((length1 - length2) / 2 / leftHypotenuse) + + points = [ + { x: pointer.x - length1 / 2 + leftHypotenuse * Math.cos(leftAngle), y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(leftAngle) }, + { 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 - rightHypotenuse * Math.cos(leftAngle), + y: pointer.y + length3 / 2 - rightHypotenuse * Math.sin(leftAngle), + }, + { + x: pointer.x + length1 / 2 - rightHypotenuse * Math.cos(leftAngle) - length2, + y: pointer.y + length3 / 2 - rightHypotenuse * Math.sin(leftAngle), + }, + ] + + break + } + case 20: { + // 좌측 빗변 + const rightHypotenuse = Math.sqrt(((length1 - length2) / 2) ** 2 + length3 ** 2) + const leftHypotenuse = (length4 / length3) * rightHypotenuse + + const rightAngle = Math.acos((length1 - length2) / 2 / rightHypotenuse) + points = [ + { + x: pointer.x + length1 / 2 - rightHypotenuse * Math.cos(rightAngle), + y: pointer.y + length3 / 2 - rightHypotenuse * Math.sin(rightAngle), + }, + { 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 + leftHypotenuse * Math.cos(rightAngle), + y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(rightAngle), + }, + { + x: pointer.x - length1 / 2 + leftHypotenuse * Math.cos(rightAngle) + length2, + y: pointer.y + length3 / 2 - leftHypotenuse * Math.sin(rightAngle), + }, + ] + break + } + case 21: { + const pointsArray = [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 0, y: 0 }, + ] + + const tmpPolygon = 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 }, + { x: 0, y: length3 }, + { x: length1 - length2, y: length3 }, + { x: (length1 - length2) / 2, y: length3 - length3 }, ], { fill: 'transparent', - stroke: 'black', + stroke: 'black', //black strokeWidth: 2, selectable: true, - fontSize: fontSize, - name: 'guideTriangle', + fontSize: 0, }, ) + + const coord = getIntersectionPoint(tmpPolygon.lines[1].startPoint, tmpPolygon.lines[2].startPoint, length3 - length4) + const scale = (length1 - length2) / coord.x + + tmpPolygon.set({ scaleX: scale }) + + pointsArray[0].x = 0 + pointsArray[0].y = length3 //바닥면부터 시작하게 + pointsArray[1].x = pointsArray[0].x + length1 + pointsArray[1].y = pointsArray[0].y + pointsArray[2].x = pointsArray[1].x + pointsArray[2].y = pointsArray[1].y - length4 + pointsArray[3].x = pointsArray[2].x - length2 + pointsArray[3].y = pointsArray[2].y + pointsArray[4].x = tmpPolygon.getCurrentPoints()[2].x + pointsArray[4].y = tmpPolygon.getCurrentPoints()[2].y + + points = [ + { + x: pointer.x - length1 / 2, + 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, + }, + { + x: pointer.x - (length2 - length1 / 2), + y: pointer.y - length4 / 2, + }, + { + x: pointer.x - length1 / 2 + pointsArray[4].x, + y: pointer.y - length3 + length4 / 2, + }, + ] + + break + } + case 22: { + const pointsArray = [ + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 0, y: 0 }, + { x: 0, y: 0 }, + ] + + const tmpPolygon = new QPolygon( + [ + { x: 0, y: length3 }, + { x: length1 - length2, y: length3 }, + { x: (length1 - length2) / 2, y: length3 - length3 }, + ], + { + fill: 'transparent', + stroke: 'black', //black + strokeWidth: 2, + selectable: true, + fontSize: 0, + }, + ) + + const coord = getIntersectionPoint(tmpPolygon.lines[1].startPoint, tmpPolygon.lines[2].startPoint, length3 - length4) + const scale = (length1 - length2) / coord.x + + tmpPolygon.set({ scaleX: scale }) + + pointsArray[0].x = 0 + pointsArray[0].y = length3 //바닥면부터 시작하게 + pointsArray[1].x = pointsArray[0].x + length1 + pointsArray[1].y = pointsArray[0].y + pointsArray[2].x = pointsArray[1].x + pointsArray[2].y = pointsArray[1].y - length4 + pointsArray[3].x = pointsArray[2].x - length2 + pointsArray[3].y = pointsArray[2].y + pointsArray[4].x = tmpPolygon.getCurrentPoints()[2].x + pointsArray[4].y = tmpPolygon.getCurrentPoints()[2].y + + points = [ + { + x: pointer.x - length1 / 2, + 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, + }, + { + x: pointer.x - (length2 - length1 / 2), + y: pointer.y - length4 / 2, + }, + { + x: pointer.x - length1 / 2 + pointsArray[4].x, + y: pointer.y - length3 + length4 / 2, + }, + ] + + break + } + case 23: { + points = [ + { 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 }, + ] + break + } + + case 24: { + points = [ + { 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 }, + ] + break + } + + case 25: { + points = [ + { + 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, + }, + ] + break + } + case 26: { + const angle = (Math.asin(length3 / length4) * 180) / Math.PI // 높이와 빗변으로 먼저 각도구하기 + + const topL = (length1 - length2) / 2 / Math.cos((angle * Math.PI) / 180) // 꺽이는부분 윗쪽 길이 + + points = [ + { + 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)), + }, + ] + break + } + case 27: { + const angle = (Math.asin(length3 / length4) * 180) / Math.PI // 높이와 빗변으로 먼저 각도구하기 + + const topL = (length1 - length2) / 2 / Math.cos((angle * Math.PI) / 180) // 꺽이는부분 윗쪽 길이 + points = [ + { + 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)), + }, + ] + break + } + case 28: { + 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) + const newAngleInRadians = (90 - angleInDegrees) * (Math.PI / 180) + points = [ + { 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), + }, + ] + break + } + case 29: { + 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) + const newAngleInRadians = (90 - angleInDegrees) * (Math.PI / 180) + points = [ + { + 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) }, + ] + break } } + + const options = { + fill: 'transparent', + stroke: 'black', + strokeWidth: 2, + selectable: true, + fontSize: fontSize, + lockMovementX: true, // X 축 이동 잠금 + lockMovementY: true, // Y 축 이동 잠금 + lockRotation: true, // 회전 잠금 + lockScalingX: true, // X 축 크기 조정 잠금 + lockScalingY: true, // Y 축 크기 조정 잠금 + name: 'guideTriangle', + flipX: type === 22 ? true : false, + } + + obj = new QPolygon(points, options) + obj.setCoords() //좌표 변경 적용 + canvas?.add(obj) + obj.set({ direction: direction }) + setCurrentPattern(obj) + canvas?.renderAll() }) canvas?.on('mouse:down', (e) => { @@ -242,138 +631,241 @@ export const SurfaceShapeModal = ({ canvas }) => { setOpen(false) } + const checkValid = () => { + let result = true + switch (type) { + case 1: { + if (length3 !== 0 && length1 > length3) { + return + } + break + } + case 3: { + if (length3 > length1) { + return + } + break + } + case 6: { + if (length3 > length1) { + return + } + break + } + case 7: { + if (length3 > length1) { + return + } + break + } + case 8: { + if (length2 > length3) { + return + } + break + } + case 9: { + if (length2 > length3) { + return + } + break + } + case 21: { + if (length1 < length2) { + alert('1번보다 작게 입력해주세요.') + return + } + + if (length3 < length4) { + alert('3번보다 작게 입력해주세요.') + return + } + } + case 22: { + if (length1 < length2) { + alert('1번보다 작게 입력해주세요.') + return + } + + if (length3 < length4) { + alert('3번보다 작게 입력해주세요.') + return + } + } + } + + return result + } + const setLength = (e) => { - const { name, value } = e.target + let { name, value } = e.target + value = value.replace(/[^-0-9]/g, '') switch (name) { case 'length1': - setLength1(value) + setLength1(Number(value)) break case 'length2': - setLength2(value) + setLength2(Number(value)) break case 'length3': - setLength3(value) + setLength3(Number(value)) break case 'length4': - setLength4(value) + setLength4(Number(value)) break case 'length5': - setLength5(value) + setLength5(Number(value)) break default: break } } + const setCurrentPattern = (polygon) => { + const { width, height, roofStyle } = roofMaterial + const roofRatio = window.devicePixelRatio || 1 + const patternSourceCanvas = document.createElement('canvas') + + if (roofStyle === 1) { + patternSourceCanvas.width = width * roofRatio + patternSourceCanvas.height = height * roofRatio + } else if (roofStyle === 2) { + patternSourceCanvas.width = width * 2 + patternSourceCanvas.height = height * 2 + } + + const ctx = patternSourceCanvas.getContext('2d') + + ctx.scale(roofRatio, roofRatio) + ctx.strokeStyle = 'green' + ctx.lineWidth = 0.4 + // 벽돌 패턴 그리기 + if (roofStyle === 1) { + ctx.strokeRect(0, 0, 50, 30) + } else if (roofStyle === 2) { + // 지그재그 + ctx.strokeRect(0, 0, 200, 100) + ctx.strokeRect(100, 100, 200, 100) + } + + // 패턴 생성 + const pattern = new fabric.Pattern({ + source: patternSourceCanvas, + repeat: 'repeat', + }) + polygon.set('fill', null) + + polygon.set('fill', pattern) + canvas?.renderAll() + } + + const ButtonComp = () => { + // Generate 29 buttons + const buttons = [] + for (let i = 1; i <= 29; i++) { + buttons.push( + , + ) + } + + return
    {buttons}
    + } + return ( <>
    면형상배치{type}
    - - - - - - - - - +
    {type === 1 ? (
    길이1 - - 길이2 - - 길이3 + + {length3 === 0 && ( + <> + 길이2 + + + )} + 대각선
    - ) : type === 2 ? ( + ) : [2, 4, 5].includes(type) ? (
    길이1 - + 길이2 - +
    - ) : type === 3 ? ( + ) : [3, 6, 7, 8, 9, 24, 28, 29].includes(type) ? ( <> 길이1 - + 길이2 - + 길이3 - + - ) : type === 4 ? ( + ) : [11, 12, 19, 20, 21, 22, 25, 26, 27].includes(type) ? ( <> 길이1 - + 길이2 - - - ) : type === 5 ? ( - <> - 길이1 - - 길이2 - - - ) : type === 6 ? ( - <> - 길이1 - - 길이2 - + 길이3 - + + 길이4 + - ) : type === 7 ? ( + ) : [10, 13, 14, 15, 16, 17, 18, 23].includes(type) ? ( <> 길이1 - + 길이2 - + 길이3 - - - ) : type === 8 ? ( - <> - 길이1 - - 길이2 - - 길이3 - - - ) : type === 9 ? ( - <> - 길이1 - - 길이2 - - 길이3 - + + 길이4 + + 길이5 + ) : ( <> )} +
    +
    + +
    +
    + + +
    +
    + +
    +