feat: Add CAD file conversion functionality to Playground component

This commit is contained in:
yoosangwook 2024-09-04 13:00:00 +09:00
parent b2b528fc07
commit 7a150be069
8 changed files with 136 additions and 15 deletions

View File

@ -1,10 +1,10 @@
NEXT_PUBLIC_TEST="테스트변수입니다. development" 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" # NEXT_PUBLIC_API_SERVER_PATH="http://localhost:8080"
DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;password=Qwertqaz12345;trustServerCertificate=true" DATABASE_URL="sqlserver://mssql.devgrr.kr:1433;database=qcast;user=qcast;password=Qwertqaz12345;trustServerCertificate=true"
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"

View File

@ -6,4 +6,4 @@ DATABASE_URL=""
SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y=" SESSION_SECRET="i3iHH1yp2/2SpQSIySQ4bpyc4g0D+zCF9FAn5xUG0+Y="
CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3" NEXT_PUBLIC_CONVERTER_API_URL="https://v2.convertapi.com/convert/dwg/to/png?Secret=secret_bV5zuYMyyIYFlOb3"

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import { useState } from 'react' import { useRef, useState } from 'react'
import { Button, Table, TableBody, TableCell, TableColumn, TableHeader, TableRow } from '@nextui-org/react' import { Button } from '@nextui-org/react'
import ColorPicker from './common/color-picker/ColorPicker' import ColorPicker from './common/color-picker/ColorPicker'
import { useAxios } from '@/hooks/useAxios' import { useAxios } from '@/hooks/useAxios'
@ -12,11 +12,16 @@ import { convertDwgToPng } from '@/lib/cadAction'
import QSelect from '@/components/ui/QSelect' import QSelect from '@/components/ui/QSelect'
import styles from './playground.module.css' import styles from './playground.module.css'
import { useRecoilState } from 'recoil'
import { cadFileNameState, useCadFileState } from '@/store/canvasAtom'
export default function Playground() { export default function Playground() {
const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState)
const [cadFileName, setCadFileName] = useRecoilState(cadFileNameState)
const fileRef = useRef(null) const fileRef = useRef(null)
const { get, post } = useAxios() const { get, promisePost } = useAxios()
const testVar = process.env.NEXT_PUBLIC_TEST const testVar = process.env.NEXT_PUBLIC_TEST
const converterUrl = process.env.NEXT_PUBLIC_CONVERTER_API_URL
const { getMessage } = useMessage() const { getMessage } = useMessage()
const [color, setColor] = useState('#ff0000') const [color, setColor] = useState('#ff0000')
@ -36,8 +41,16 @@ export default function Playground() {
const formData = new FormData() const formData = new FormData()
formData.append('file', fileRef.current.files[0]) formData.append('file', fileRef.current.files[0])
const result = await post({ url: process.env.CONVERTER_API_URL, data: formData }) await promisePost({ url: converterUrl, data: formData })
await convertDwgToPng(result.Files[0].FileName, result.Files[0].FileData) .then((res) => {
console.log('response: ', res)
convertDwgToPng(res.data.Files[0].FileName, res.data.Files[0].FileData)
setUseCadFile(true)
setCadFileName(res.data.Files[0].FileName)
})
.catch((err) => {
console.error(err)
})
} }
const data = [ const data = [

View File

@ -1,7 +1,7 @@
'use client' 'use client'
import { useCanvas } from '@/hooks/useCanvas' 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 { v4 as uuidv4 } from 'uuid'
import { useMode } from '@/hooks/useMode' import { useMode } from '@/hooks/useMode'
import { Mode } from '@/common/common' import { Mode } from '@/common/common'
@ -9,6 +9,8 @@ import { Button } from '@nextui-org/react'
import RangeSlider from './ui/RangeSlider' import RangeSlider from './ui/RangeSlider'
import { useRecoilState, useRecoilValue } from 'recoil' import { useRecoilState, useRecoilValue } from 'recoil'
import { import {
cadFileCompleteState,
cadFileNameState,
canvasSizeState, canvasSizeState,
compassState, compassState,
currentObjectState, currentObjectState,
@ -17,6 +19,7 @@ import {
roofState, roofState,
sortedPolygonArray, sortedPolygonArray,
templateTypeState, templateTypeState,
useCadFileState,
wallState, wallState,
} from '@/store/canvasAtom' } from '@/store/canvasAtom'
import { QLine } from '@/components/fabric/QLine' import { QLine } from '@/components/fabric/QLine'
@ -38,7 +41,18 @@ import { SurfaceShapeModal } from '@/components/ui/SurfaceShape'
export default function Roof2(props) { export default function Roof2(props) {
const { name, userId, email, isLoggedIn } = props const { name, userId, email, isLoggedIn } = props
const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas } = useCanvas('canvas') const {
canvas,
handleRedo,
handleUndo,
setCanvasBackgroundWithDots,
saveImage,
addCanvas,
handleCadImageLoad,
handleCadImageInit,
backImg,
setBackImg,
} = useCanvas('canvas')
const { get } = useAxios() const { get } = useAxios()
@ -84,6 +98,13 @@ export default function Roof2(props) {
canvas, canvas,
} }
// cad
const [useCadFile, setUseCadFile] = useRecoilState(useCadFileState)
const [cadFileName, setCadFileName] = useRecoilState(cadFileNameState)
const [cadFileComplete, setCadFileComplete] = useRecoilState(cadFileCompleteState)
let imgPath
useCadFile && (imgPath = `/cadImages/${cadFileName}`)
const { const {
mode, mode,
setMode, setMode,
@ -130,6 +151,11 @@ export default function Roof2(props) {
return return
} }
changeMode(canvas, mode) changeMode(canvas, mode)
if (!cadFileComplete && useCadFile) {
// cad
useCadFile && handleCadImageLoad(imgPath, canvas)
}
}, [canvas, mode]) }, [canvas, mode])
const makeLine = () => { const makeLine = () => {
@ -757,6 +783,32 @@ export default function Roof2(props) {
<Button className="m-1 p-2" onClick={drawCellInTrestle}> <Button className="m-1 p-2" onClick={drawCellInTrestle}>
모듈 채우기 모듈 채우기
</Button> </Button>
<Button
className="m-1 p-2"
onClick={() => {
setUseCadFile(false)
setCadFileName('')
handleCadImageInit()
}}
>
cad 파일 초기화
</Button>
<Button
className="m-1 p-2"
onClick={() => {
backImg
.set({
selectable: false,
})
.sendToBack()
canvas.clear()
canvas.add(backImg)
canvas.renderAll()
setCadFileComplete(true)
}}
>
cad 파일 조정 완료
</Button>
<Button className="m-1 p-2" color={`${showControl ? 'primary' : 'default'}`} onClick={handleShowController}> <Button className="m-1 p-2" color={`${showControl ? 'primary' : 'default'}`} onClick={handleShowController}>
canvas 컨트롤러 {`${showControl ? '숨기기' : '보이기'}`} canvas 컨트롤러 {`${showControl ? '숨기기' : '보이기'}`}
</Button> </Button>

View File

@ -45,6 +45,10 @@ export function useAxios() {
.catch(console.error) .catch(console.error)
} }
const promisePost = async ({ url, data }) => {
return await getInstances(url).post(url, data)
}
const put = async ({ url, data }) => { const put = async ({ url, data }) => {
return await getInstances(url) return await getInstances(url)
.put(url, data) .put(url, data)
@ -66,5 +70,5 @@ export function useAxios() {
.catch(console.error) .catch(console.error)
} }
return { get, post, put, patch, del } return { get, post, promisePost, put, patch, del }
} }

View File

@ -17,6 +17,7 @@ export function useCanvas(id) {
const [canvas, setCanvas] = useState() const [canvas, setCanvas] = useState()
const [isLocked, setIsLocked] = useState(false) const [isLocked, setIsLocked] = useState(false)
const [history, setHistory] = useState([]) const [history, setHistory] = useState([])
const [backImg, setBackImg] = useState()
const [canvasSize] = useRecoilState(canvasSizeState) const [canvasSize] = useRecoilState(canvasSizeState)
const [fontSize] = useRecoilState(fontSizeState) const [fontSize] = useRecoilState(fontSizeState)
const { setCanvasForEvent, attachDefaultEventOnCanvas } = useCanvasEvent() const { setCanvasForEvent, attachDefaultEventOnCanvas } = useCanvasEvent()
@ -444,6 +445,30 @@ export function useCanvas(id) {
// }, 1000) // }, 1000)
} }
/**
* cad 파일 사용시 이미지 로딩 함수
*/
const handleCadImageLoad = (url) => {
console.log('image load url: ', url)
fabric.Image.fromURL(url, function (img) {
img.set({
left: 0,
top: 0,
width: 1500,
height: 1500,
selectable: true,
})
canvas.add(img)
canvas.renderAll()
setBackImg(img)
})
}
const handleCadImageInit = () => {
canvas.clear()
}
return { return {
canvas, canvas,
addShape, addShape,
@ -459,5 +484,9 @@ export function useCanvas(id) {
setCanvasBackgroundWithDots, setCanvasBackgroundWithDots,
addCanvas, addCanvas,
removeMouseLines, removeMouseLines,
handleCadImageLoad,
handleCadImageInit,
backImg,
setBackImg,
} }
} }

View File

@ -1,13 +1,18 @@
'use server'
import fs from 'fs/promises'
const imageSavePath = 'public/cadImages'
const convertDwgToPng = async (fileName, data) => { const convertDwgToPng = async (fileName, data) => {
console.log('fileName', fileName) console.log('fileName', fileName)
const imagePath = 'public/cad-images'
try { try {
await fs.readdir(imagePath) await fs.readdir(imageSavePath)
} catch { } catch {
await fs.mkdir(imagePath) await fs.mkdir(imageSavePath)
} }
return fs.writeFile(`${imagePath}/${fileName}`, data, 'base64') return await fs.writeFile(`${imageSavePath}/${fileName}`, data, 'base64')
} }
export { convertDwgToPng } export { convertDwgToPng }

View File

@ -126,3 +126,21 @@ export const customSettingsState = atom({
default: {}, default: {},
dangerouslyAllowMutability: true, dangerouslyAllowMutability: true,
}) })
// cad 도면 파일 사용 여부
export const useCadFileState = atom({
key: 'useCadFile',
default: false,
})
// cad 도면 파일 이름
export const cadFileNameState = atom({
key: 'cadFileName',
default: '',
})
// cad 도면 파일 조정 완료
export const cadFileCompleteState = atom({
key: 'cadFileComplete',
default: false,
})