222 lines
7.7 KiB
JavaScript

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,
}
}