'use client' import { useCanvas } from '@/hooks/useCanvas' import { useEffect, useRef, useState } from 'react' import { v4 as uuidv4 } from 'uuid' import { useMode } from '@/hooks/useMode' import { LINE_TYPE, Mode } from '@/common/common' import { Button } 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 } from '@/util/canvas-util' import { QPolygon } from '@/components/fabric/QPolygon' 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 InitSettingsModal from './InitSettingsModal' import GridSettingsModal from './GridSettingsModal' import { SurfaceShapeModal } from '@/components/ui/SurfaceShape' import { changeCurrentRoof, drawDirectionStringToArrow } from '@/util/qpolygon-utils' import ThumbnailList from '@/components/ui/ThumbnailLIst' import ObjectPlacement from '@/components/ui/ObjectPlacement' import { globalLocaleStore } from '@/store/localeAtom' export default function Roof2(props) { const { name, userId, email, isLoggedIn } = props const { canvas, handleRedo, handleUndo, setCanvasBackgroundWithDots, saveImage, addCanvas, handleBackImageLoadToCanvas, handleCadImageInit, backImg, setBackImg, } = useCanvas('canvas') const globalLocaleState = useRecoilValue(globalLocaleStore) const { get } = useAxios(globalLocaleState) const canvasRef = useRef(null) //canvas 기본 사이즈 const [canvasSize, setCanvasSize] = useRecoilState(canvasSizeState) //canvas 가로 사이즈 const [verticalSize, setVerticalSize] = useState(canvasSize.vertical) //canvas 세로 사이즈 const [horizontalSize, setHorizontalSize] = useState(canvasSize.horizontal) // 글자크기 const [fontSize, setFontSize] = useRecoilState(fontSizeState) const [sortedArray] = useRecoilState(sortedPolygonArray) const [angle, setAngle] = useState(0) const [showControl, setShowControl] = useState(false) //지붕재 const roofMaterial = useRecoilValue(roofMaterialState) const [templateType, setTemplateType] = useRecoilState(templateTypeState) const [compass, setCompass] = useRecoilState(compassState) const roof = useRecoilValue(roofState) const wall = useRecoilValue(wallState) const [open, setOpen] = useRecoilState(modalState) const [contents, setContent] = useRecoilState(modalContent) const [scale, setScale] = useState(1) const currentObject = useRecoilValue(currentObjectState) //canvas 썸네일 const [thumbnails, setThumbnails] = useState([]) const thumbnailProps = { thumbnails, 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, changeMode, handleClear, zoomIn, zoomOut, zoom, togglePolygonLine, handleOuterlinesTest, handleOuterlinesTest2, applyTemplateB, makeRoofPatternPolygon, createRoofRack, drawRoofPolygon, drawCellInTrestle, drawCellManualInTrestle, setDirectionTrestles, cutHelpLines, } = useMode() // const [canvasState, setCanvasState] = useRecoilState(canvasAtom) useEffect(() => { get({ url: `/api/canvas-management/canvas-statuses/by-object/test123240822001/${userId}` }).then((res) => { // console.log(res) const arrangeData = res.map((item) => { // console.log(item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '')) const test = item.canvasStatus.replace(/##/g, '"').replace(/\\/g, '') const test2 = test.substring(1, test.length - 1) return { id: item.id, userId: item.userId, imageName: `/canvasState/${item.imageName}.png`, canvasStatus: JSON.stringify(test2), } }) setThumbnails(arrangeData) }) }, []) useEffect(() => { if (!canvas) { return } changeMode(canvas, mode) if (!cadFileComplete && useCadFile) { // cad 파일 로드 useCadFile && handleBackImageLoadToCanvas(imgPath, canvas) } if (useGoogleMapFile) { handleBackImageLoadToCanvas(imgPath, canvas) } }, [canvas, mode]) const makeLine = () => { if (canvas) { const line = new QLine([50, 50, 200, 50], { stroke: 'black', selectable: true, strokeWidth: 2, fontSize: fontSize, }) canvas?.add(line) } } const makePolygon = () => { if (canvas) { const polygon = new QPolygon( [ { x: 100, y: 100 }, { x: 600, y: 200 }, { x: 700, y: 800 }, { x: 100, y: 800 }, ], { fill: 'transparent', stroke: 'black', strokeWidth: 2, selectable: true, fontSize: fontSize, }, ) canvas?.add(polygon) // polygon.fillCell({ width: 50, height: 30, padding: 10 }) } } useEffect(() => { setCanvasSize({ ...canvasSize, vertical: parseInt(verticalSize), horizontal: parseInt(horizontalSize) }) }, [verticalSize, horizontalSize]) /** * 값 변경시 */ // useEffect(() => { // canvasSizeMode() // }, [verticalSize, horizontalSize]) useEffect(() => { const { vertical, horizontal } = canvasSize if (vertical !== verticalSize || horizontal !== horizontalSize) { canvas?.setWidth(horizontalSize) canvas?.setHeight(verticalSize) canvas?.renderAll() } }, [canvasSize, canvas]) const makeQPolygon = () => { const type1 = [ { x: 100, y: 100 }, { x: 850, y: 100 }, { x: 850, y: 800 }, { x: 500, y: 800 }, { x: 500, y: 400 }, { x: 100, y: 400 }, ] const type2 = [ { x: 200, y: 100 }, { x: 200, y: 1000 }, { x: 1100, y: 1000 }, { x: 1100, y: 600 }, { x: 650, y: 600 }, { x: 650, y: 100 }, ] const type3 = [ { x: 200, y: 100 }, { x: 200, y: 800 }, { x: 500, y: 800 }, { x: 500, y: 300 }, { x: 800, y: 300 }, { x: 800, y: 100 }, ] const type4 = [ { x: 150, y: 450 }, { x: 150, y: 800 }, { x: 750, y: 800 }, { x: 750, y: 300 }, { x: 550, y: 300 }, { x: 550, y: 450 }, ] const type1A = [ { x: 67, y: 81 }, { x: 67, y: 660 }, { x: 437, y: 660 }, { x: 437, y: 1190 }, { x: 858, y: 1190 }, { x: 858, y: 81 }, ] const type1B = [ { x: 137, y: 42 }, { x: 137, y: 621 }, { x: 667, y: 621 }, { x: 667, y: 991 }, { x: 1088, y: 991 }, { x: 1088, y: 42 }, ] const eightPoint = [ { x: 240.1111, y: 130.1111 }, { x: 240.1111, y: 630.1111 }, { x: 640.1111, y: 630.1111 }, { x: 640.1111, y: 480.1111 }, { x: 440.1111, y: 480.1111 }, { x: 440.1111, y: 280.1111 }, { x: 740.1111, y: 280.1111 }, { x: 740.1111, y: 130.1111 }, ] const eightPoint2 = [ { x: 197, y: 215 }, { x: 197, y: 815 }, { x: 397, y: 815 }, { x: 397, y: 1115 }, { x: 697, y: 1115 }, { x: 697, y: 815 }, { x: 897, y: 815 }, { x: 897, y: 215 }, ] const eightPoint3 = [ { x: 190, y: 147 }, { x: 190, y: 747 }, { x: 490, y: 747 }, { x: 490, y: 497 }, { x: 640, y: 497 }, { x: 640, y: 747 }, { x: 1090, y: 747 }, { x: 1090, y: 147 }, ] const eightPoint4 = [ { x: 200, y: 200 }, { x: 200, y: 400 }, { x: 500, y: 400 }, { x: 500, y: 700 }, { x: 800, y: 700 }, { x: 800, y: 400 }, { x: 1100, y: 400 }, { x: 1100, y: 200 }, ] const eightPoint5 = [ { x: 140, y: 101 }, { x: 140, y: 601 }, { x: 440, y: 601 }, { x: 440, y: 801 }, { x: 840, y: 801 }, { x: 840, y: 601 }, { x: 1140, y: 601 }, { x: 1140, y: 101 }, ] const twelvePoint = [ { x: 195, y: 166 }, { x: 195, y: 466 }, { x: 395, y: 466 }, { x: 395, y: 766 }, { x: 545, y: 766 }, { x: 545, y: 466 }, { x: 695, y: 466 }, { x: 695, y: 666 }, { x: 845, y: 666 }, { x: 845, y: 466 }, { x: 995, y: 466 }, { x: 995, y: 166 }, ] const twelvePoint2 = [ { x: 165, y: 81 }, { x: 165, y: 1081 }, { x: 465, y: 1081 }, { x: 465, y: 781 }, { x: 765, y: 781 }, { x: 765, y: 1081 }, { x: 1065, y: 1081 }, { x: 1065, y: 581 }, { x: 765, y: 581 }, { x: 765, y: 281 }, { x: 1065, y: 281 }, { x: 1065, y: 81 }, ] const complicatedType = [ { x: 100, y: 100 }, { x: 100, y: 1100 }, { x: 400, y: 1100 }, { x: 400, y: 800 }, { x: 700, y: 800 }, { x: 700, y: 1100 }, { x: 1000, y: 1100 }, { x: 1000, y: 600 }, { x: 700, y: 600 }, { x: 700, y: 300 }, { x: 1000, y: 300 }, { x: 1000, y: 100 }, ] const testType = [ { x: 500, y: 400 }, { x: 650, y: 550 }, { x: 575, y: 625 }, { x: 325, y: 625 }, { x: 100, y: 400 }, ] const testType2 = [ { x: 100, y: 400 }, { x: 325, y: 625 }, { x: 575, y: 625 }, { x: 650, y: 550 }, { x: 500, y: 400 }, ] const triangleType = [ { x: 100, y: 100 }, { x: 100, y: 600 }, { x: 600, y: 600 }, { x: 600, y: 100 }, ] const rectangleType1 = [ { x: 500, y: 100 }, { x: 500, y: 800 }, { x: 900, y: 800 }, { x: 900, y: 100 }, ] const rectangleType2 = [ { x: 100, y: 100 }, { x: 100, y: 300 }, { x: 600, y: 300 }, { x: 600, y: 100 }, ] const types = [type1, type2, type3, type4, type1A, type1B, eightPoint, eightPoint2, eightPoint3, eightPoint4, twelvePoint] const newP = [ { x: 450, y: 450 }, { x: 650, y: 250 }, { x: 675, y: 275 }, { x: 450, y: 850 }, ] const test1 = [ { x: 381, y: 178 }, { x: 381, y: 659.3 }, { x: 773.3, y: 659.3 }, { x: 773.3, y: 497.9 }, { x: 1457, y: 497.9 }, { x: 1457, y: 178 }, ] const test2 = [ { x: 113, y: 114.9 }, { x: 113, y: 371.9 }, { x: 762, y: 371.9 }, { x: 762, y: 818.7 }, { x: 1478.6, y: 818.7 }, { x: 1478.6, y: 114.9 }, ] const test3 = [ { x: 100, y: 100 }, { x: 100, y: 600 }, { x: 600, y: 600 }, { x: 600, y: 100 }, { x: 500, y: 100 }, { x: 500, y: 200 }, { x: 200, y: 200 }, { x: 200, y: 100 }, ] const test4 = [ { x: 100, y: 100 }, { x: 100, y: 1000 }, { x: 1100, y: 1000 }, { x: 1100, y: 550 }, { x: 500, y: 550 }, { x: 500, y: 100 }, ] const polygon = new QPolygon(test4, { fill: 'transparent', stroke: 'green', strokeWidth: 1, selectable: false, fontSize: fontSize, name: 'wall', }) canvas?.add(polygon) handleOuterlinesTest2(polygon, 50) setTemplateType(1) } const rotateShape = () => { if (canvas) { const activeObject = canvas?.getActiveObject() if (activeObject) { activeObject.rotate(angle) canvas?.renderAll() } } } const makeQLine = () => { if (canvas) { const line = new QLine([50, 250, 900, 250], { stroke: 'black', strokeWidth: 5, fontSize: fontSize, selectable: true, }) const line2 = new QLine([450, 450, 821, 78], { stroke: 'black', strokeWidth: 5, fontSize: fontSize, selectable: true, }) canvas?.add(line) canvas?.add(line2) const interSectionPoint = calculateIntersection(line, line2) if (interSectionPoint) { const circle = new fabric.Circle({ radius: 5, fill: 'red', left: interSectionPoint.x - 5, top: interSectionPoint.y - 5, }) canvas?.add(circle) } } } const addBackgroundInPolygon = (polygon) => { fabric.Image.fromURL('assets/img/check2.png', function (img) { // 패턴 객체를 생성합니다. const pattern = new fabric.Pattern({ source: img.getElement(), repeat: 'repeat', }) polygon.fillBackground(pattern) }) } function PolygonToLine() { const polygon = canvas?.getActiveObject() if (polygon.type !== 'QPolygon') { return } const lines = togglePolygonLine(polygon) } /** * canvas 내용 저장하기 */ const handleSaveCanvas = async () => { // const jsonStr = JSON.stringify(canvas?.toDatalessJSON(['type', 'fontSize'])) const jsonObj = JSON.stringify(canvas?.toDatalessJSON(['type', 'fontSize', 'lines'])) console.log(jsonObj) const param = { loginId: 'test', canvas: jsonObj, } console.log(param) await insertCanvasState(param) handleClear() } const drawRoofMaterial = () => { const { width, height, roofStyle } = roofMaterial const wallPolygon = canvas?.getObjects().find((obj) => obj.name === 'wall') wallPolygon.set('strokeDashArray', [10, 5, 2, 5]) wallPolygon.set('stroke', 'blue') wallPolygon.set('strokeWidth', 1) const roofs = canvas?.getObjects().filter((obj) => obj.name === 'roof') roofs.forEach((roof) => { let maxLengthLine = roof.lines.reduce((acc, cur) => { return acc.length > cur.length ? acc : cur }) const roofRatio = window.devicePixelRatio || 1 // 패턴 소스를 위한 임시 캔버스 생성 const patternSourceCanvas = document.createElement('canvas') if (roofStyle === 1) { if (maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left') { patternSourceCanvas.width = width * roofRatio patternSourceCanvas.height = height * roofRatio } else { patternSourceCanvas.width = height * roofRatio patternSourceCanvas.height = width * roofRatio } } else if (roofStyle === 2) { if (maxLengthLine.direction === 'right' || maxLengthLine.direction === 'left') { patternSourceCanvas.width = width * 2 patternSourceCanvas.height = height * 2 } else { patternSourceCanvas.width = height * 2 patternSourceCanvas.height = width * 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', }) roof.set('fill', null) roof.set('fill', pattern) canvas?.renderAll() }) } /** * canvas 내용 불러오기 */ const handleLoadCanvas = async () => { const canvasStates = await getCanvasState() console.log(JSON.parse(canvasStates.canvas)) canvas?.loadFromJSON(JSON.parse(canvasStates.canvas)) } /** * 컨트롤러 보이기/숨기기 */ const handleShowController = () => { setShowControl(!showControl) } const drawRoofPatterns = (roofStyle) => { makeRoofPatternPolygon(roofStyle) } const deleteCell = () => { const selectedCells = canvas?.getObjects().filter((obj) => obj.name === 'cell' && obj.selected) selectedCells.forEach((cell) => { canvas?.remove(cell) }) } const setCompassState = (degree) => { setCompass(degree) } const changeLength = (e) => { const polygon = canvas?.getActiveObject() if (polygon?.type !== 'QPolygon') { return } setScale(e) polygon.setScaleX(e) canvas?.renderAll() } const moduleConfiguration = () => { createRoofRack() } const setDirectionStringToArrow = () => { drawDirectionStringToArrow(canvas, globalCampass) /** * 나중에 유틸로 다시 구현 */ // const groupShapes = canvas?.getObjects().filter((obj) => obj.name === 'cellGroup') // console.log('groupShapes', groupShapes) // groupShapes.forEach((obj) => { // let originAngle = obj._objects.find((array) => array.originAngle !== undefined).originAngle // console.log('originAngle', originAngle) // let rotateAngle = globalCampass // // let rotateAngle = originAngle + globalCampass // // if (rotateAngle > 360) { // // rotateAngle -= 360 // // } // console.log('rotateAngle', rotateAngle) // obj.set({ angle: rotateAngle, originX: 'center', originY: 'center' }) // obj.setCoords() // }) canvas?.renderAll() } const setHipRoof = () => { const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof') const currentRoof = polygon.lines[2] currentRoof.attributes.type = LINE_TYPE.WALLLINE.EAVES currentRoof.attributes.offset = 50 changeCurrentRoof(currentRoof, canvas) } const setGableRoof = () => { const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof') const currentRoof = polygon.lines[2] currentRoof.attributes.type = LINE_TYPE.WALLLINE.GABLE currentRoof.attributes.offset = 30 changeCurrentRoof(currentRoof, canvas) } const setHipAndGableRoof = () => { let offset = Number(prompt('팔작지붕 폭', '50')) if (!isNaN(offset) && offset > 0) { const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof') const currentRoof = polygon.lines[2] currentRoof.attributes.type = LINE_TYPE.WALLLINE.HIPANDGABLE currentRoof.attributes.width = offset changeCurrentRoof(currentRoof, canvas) } else { alert('폭은 0 보다 커야 함') } } const setJerkInHeadRoof = () => { let offset = Number(prompt('팔작지붕 폭', '50')) if (!isNaN(offset) && offset > 0) { const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof') const currentRoof = polygon.lines[2] currentRoof.attributes.type = LINE_TYPE.WALLLINE.JERKINHEAD currentRoof.attributes.width = offset changeCurrentRoof(currentRoof, canvas) } else { alert('폭은 0 보다 커야 함') } } const setWallRoof = () => { let offset = Number(prompt('소매 폭', '0')) const polygon = canvas?.getObjects().find((obj) => obj.name === 'roof') const currentRoof = polygon.lines[2] currentRoof.attributes.type = LINE_TYPE.WALLLINE.WALL currentRoof.attributes.width = offset changeCurrentRoof(currentRoof, canvas) } return ( <> {canvas && ( <>
각도 입력(0~360) 후 방향설정 클릭
{ const val = e.target.value.replace(/[^-0-9]/g, '') if (val < 0 || val > 360) { setGlobalCampass(0) } else { setGlobalCampass(Number(val)) } }} />