import { useRecoilValue } from 'recoil' import { canvasState, canvasZoomState, dotLineGridSettingState } from '@/store/canvasAtom' import { useEffect } from 'react' import { gridColorState } from '@/store/gridAtom' import { gridDisplaySelector } from '@/store/settingAtom' const GRID_PADDING = 5 export function useGrid() { const canvas = useRecoilValue(canvasState) const dotLineGridSetting = useRecoilValue(dotLineGridSettingState) const gridColor = useRecoilValue(gridColorState) const isGridDisplay = useRecoilValue(gridDisplaySelector) const zoom = useRecoilValue(canvasZoomState) useEffect(() => { if (!canvas) { return } const patternData = { dotGridDisplay: dotLineGridSetting.DOT, lineGridDisplay: dotLineGridSetting.LINE, gridType: dotLineGridSetting.INTERVAL.type, gridHorizon: (dotLineGridSetting.INTERVAL.horizontalInterval / 10) * (dotLineGridSetting.INTERVAL.dimension ?? 1), gridVertical: (dotLineGridSetting.INTERVAL.verticalInterval / 10) * (dotLineGridSetting.INTERVAL.dimension ?? 1), gridRatio: dotLineGridSetting.INTERVAL.ratioInterval / 10, gridDimen: dotLineGridSetting.INTERVAL.dimension, } // 1. 점.선 그리드 설정으로 만들어진 기존 오브젝트 제거 canvas ?.getObjects() .filter((obj) => ['lineGrid', 'dotGrid'].includes(obj.name)) .forEach((obj) => canvas?.remove(obj)) //const horizontalInterval = interval.horizontalInterval //const verticalInterval = interval.verticalInterval if (patternData.dotGridDisplay) { const canvasWidth = canvas.getWidth() const canvasHeight = canvas.getHeight() const currentZoom = canvas.getZoom() const viewportTransform = canvas.viewportTransform const visibleLeft = -viewportTransform[4] / currentZoom const visibleTop = -viewportTransform[5] / currentZoom const visibleRight = visibleLeft + canvasWidth / currentZoom const visibleBottom = visibleTop + canvasHeight / currentZoom const padding = 200 // 원점(0,0) 기준 그리드 간격의 배수로 정렬하여 줌 시 위치 고정 const gridLeft = Math.floor((visibleLeft - padding) / patternData.gridHorizon) * patternData.gridHorizon const gridTop = Math.floor((visibleTop - padding) / patternData.gridVertical) * patternData.gridVertical const gridRight = visibleRight + padding const gridBottom = visibleBottom + padding const horizontalCount = Math.ceil((gridRight - gridLeft) / patternData.gridHorizon) + 1 const verticalCount = Math.ceil((gridBottom - gridTop) / patternData.gridVertical) + 1 for (let i = 0; i < horizontalCount; i++) { for (let j = 0; j < verticalCount; j++) { const dot = new fabric.Circle({ left: gridLeft + i * patternData.gridHorizon, top: gridTop + j * patternData.gridVertical, radius: 2, fill: 'red', stroke: null, strokeWidth: 0, originX: 'center', originY: 'center', selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, name: 'dotGrid', padding: GRID_PADDING, visible: isGridDisplay, }) canvas.add(dot) dot.sendToBack() } } canvas.renderAll() } if (patternData.lineGridDisplay) { // 캔버스의 실제 보이는 영역 계산 const canvasWidth = canvas.getWidth() const canvasHeight = canvas.getHeight() const currentZoom = canvas.getZoom() const viewportTransform = canvas.viewportTransform const visibleLeft = -viewportTransform[4] / currentZoom const visibleTop = -viewportTransform[5] / currentZoom const visibleRight = visibleLeft + canvasWidth / currentZoom const visibleBottom = visibleTop + canvasHeight / currentZoom // 여유 공간 추가 const padding = 200 // 원점(0,0) 기준 그리드 간격의 배수로 정렬하여 줌 시 위치 고정 const gridLeft = Math.floor((visibleLeft - padding) / patternData.gridHorizon) * patternData.gridHorizon const gridTop = Math.floor((visibleTop - padding) / patternData.gridVertical) * patternData.gridVertical const gridRight = visibleRight + padding const gridBottom = visibleBottom + padding // 가로선 생성 (수평선) const horizontalGridRange = gridBottom - gridTop const horizontalGridCount = Math.ceil(horizontalGridRange / patternData.gridVertical) + 2 for (let i = 0; i < horizontalGridCount; i++) { const y = gridTop + i * patternData.gridVertical const horizontalLine = new fabric.Line( [gridLeft, y, gridRight, y], { stroke: gridColor, strokeWidth: 1, selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, name: 'lineGrid', strokeDashArray: [5, 2], opacity: 0.3, padding: GRID_PADDING, direction: 'horizontal', visible: isGridDisplay, }, ) canvas.add(horizontalLine) } // 세로선 생성 (수직선) const verticalGridRange = gridRight - gridLeft const verticalGridCount = Math.ceil(verticalGridRange / patternData.gridHorizon) + 2 for (let i = 0; i < verticalGridCount; i++) { const x = gridLeft + i * patternData.gridHorizon const verticalLine = new fabric.Line( [x, gridTop, x, gridBottom], { stroke: gridColor, strokeWidth: 1, selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, name: 'lineGrid', strokeDashArray: [5, 2], opacity: 0.3, padding: GRID_PADDING, direction: 'vertical', visible: isGridDisplay, }, ) canvas.add(verticalLine) } } canvas.renderAll() }, [dotLineGridSetting, zoom]) const move = (object, x, y) => { object.set({ ...object, x1: object.direction === 'vertical' ? object.x1 + x : -1500, x2: object.direction === 'vertical' ? object.x1 + x : 2500, y1: object.direction === 'vertical' ? -1500 : object.y1 + y, y2: object.direction === 'vertical' ? 2500 : object.y1 + y, }) } const copy = (object, length) => { const lineStartX = object.direction === 'vertical' ? object.x1 + length : -1500 const lineEndX = object.direction === 'vertical' ? object.x2 + length : 2500 const lineStartY = object.direction === 'vertical' ? -1500 : object.y1 + length const lineEndY = object.direction === 'vertical' ? 2500 : object.y1 + length const line = new fabric.Line([lineStartX, lineStartY, lineEndX, lineEndY], { stroke: gridColor, strokeWidth: 1, selectable: true, lockMovementX: true, lockMovementY: true, lockRotation: true, lockScalingX: true, lockScalingY: true, strokeDashArray: [5, 2], opacity: 0.3, padding: GRID_PADDING, direction: object.direction, visible: isGridDisplay, name: object.name, }) canvas.add(line) canvas.setActiveObject(line) canvas.renderAll() } const removeGrid = () => { canvas .getObjects() .filter((obj) => ['lineGrid', 'dotGrid', 'tempGrid'].includes(obj.name)) .forEach((obj) => canvas.remove(obj)) canvas.renderAll() } return { move, copy, removeGrid, } }