import { useRecoilValue, useResetRecoilState } from 'recoil' import { canvasState, currentObjectState } from '@/store/canvasAtom' import { selectedRoofMaterialSelector } from '@/store/settingAtom' import { ROOF_MATERIAL_LAYOUT } from '@/components/floor-plan/modal/placementShape/PlacementShapeSetting' import { POLYGON_TYPE } from '@/common/common' import { useEvent } from '@/hooks/useEvent' import { useLine } from '@/hooks/useLine' import { outerLinePointsState } from '@/store/outerLineAtom' import { usePolygon } from '@/hooks/usePolygon' import { useText } from '@/hooks/useText' const ROOF_COLOR = { 0: 'rgb(199,240,213)', 1: 'rgb(178,238,255)', 2: 'rgb(187,204,255)', 3: 'rgb(228,202,255)', } export function useRoofFn() { const canvas = useRecoilValue(canvasState) const selectedRoofMaterial = useRecoilValue(selectedRoofMaterialSelector) const currentObject = useRecoilValue(currentObjectState) const { addCanvasMouseEventListener, initEvent } = useEvent() const resetPoints = useResetRecoilState(outerLinePointsState) const { addPitchText } = useLine() const { setPolygonLinesActualSize } = usePolygon() const { changeCorridorDimensionText } = useText() //면형상 선택 클릭시 지붕 패턴 입히기 function setSurfaceShapePattern(polygon, mode = 'onlyBorder', trestleMode = false, roofMaterial, isForceChange = false, isDisplay = false) { try { if (!polygon) { return } if (polygon.wall) { return } if (polygon.points.length < 3) { return } if (isForceChange && !isDisplay) { /*if (polygon.roofMaterial) { polygon.roofMaterial = null }*/ } if (!roofMaterial) { roofMaterial = polygon.roofMaterial ?? selectedRoofMaterial } const ratio = window.devicePixelRatio || 1 const layout = roofMaterial.layout let width = (roofMaterial.width || 226) / 10 let height = (roofMaterial.length || 158) / 10 const index = roofMaterial.index ?? 0 let roofStyle = 2 const inputPatternSize = { width: width, height: height } //임시 사이즈 const patternSize = { ...inputPatternSize } // 입력된 값을 뒤집기 위해 if (polygon.direction === 'east' || polygon.direction === 'west') { //세로형이면 width height를 바꿈 ;[patternSize.width, patternSize.height] = [inputPatternSize.height, patternSize.width] } // 패턴 소스를 위한 임시 캔버스 생성 const patternSourceCanvas = document.createElement('canvas') patternSourceCanvas.width = polygon.width * ratio patternSourceCanvas.height = polygon.height * ratio const ctx = patternSourceCanvas.getContext('2d') let offset = roofStyle === 1 ? 0 : patternSize.width / 2 const rows = Math.floor(patternSourceCanvas.height / patternSize.height) const cols = Math.floor(patternSourceCanvas.width / patternSize.width) ctx.strokeStyle = mode === 'allPainted' ? 'black' : ROOF_COLOR[index] ctx.lineWidth = 2 ctx.fillStyle = mode === 'allPainted' ? 'rgba(0, 159, 64, 0.7)' : 'white' if (trestleMode) { ctx.strokeStyle = 'black' ctx.lineWidth = 0.2 ctx.fillStyle = 'rgba(0, 0, 0, 0.1)' } else { ctx.fillStyle = 'rgba(255, 255, 255, 1)' } if (polygon.direction === 'east' || polygon.direction === 'west') { offset = roofStyle === 1 ? 0 : patternSize.height / 2 for (let col = 0; col <= cols; col++) { const x = col * patternSize.width const yStart = 0 const yEnd = patternSourceCanvas.height ctx.beginPath() ctx.moveTo(x, yStart) // 선 시작점 ctx.lineTo(x, yEnd) // 선 끝점 ctx.stroke() if (mode === 'allPainted' || trestleMode) { ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) } for (let row = 0; row <= rows; row++) { const y = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? row * patternSize.height + (col % 2 === 0 ? 0 : offset) : row * patternSize.height const xStart = col * patternSize.width const xEnd = xStart + patternSize.width ctx.beginPath() ctx.moveTo(xStart, y) // 선 시작점 ctx.lineTo(xEnd, y) // 선 끝점 ctx.stroke() if (mode === 'allPainted' || trestleMode) { ctx.fillRect(xStart, y, xEnd - xStart, patternSize.height) } } } } else { for (let row = 0; row <= rows; row++) { const y = row * patternSize.height ctx.beginPath() ctx.moveTo(0, y) // 선 시작점 ctx.lineTo(patternSourceCanvas.width, y) // 선 끝점 ctx.stroke() if (mode === 'allPainted' || trestleMode) { ctx.fillRect(0, y, patternSourceCanvas.width, patternSize.height) } for (let col = 0; col <= cols; col++) { const x = layout === ROOF_MATERIAL_LAYOUT.STAIRS ? col * patternSize.width + (row % 2 === 0 ? 0 : offset) : col * patternSize.width const yStart = row * patternSize.height const yEnd = yStart + patternSize.height ctx.beginPath() ctx.moveTo(x, yStart) // 선 시작점 ctx.lineTo(x, yEnd) // 선 끝점 ctx.stroke() if (mode === 'allPainted' || trestleMode) { ctx.fillRect(x, yStart, patternSize.width, yEnd - yStart) } } } } const hachingPatternSourceCanvas = document.createElement('canvas') if (mode === 'lineHatch') { hachingPatternSourceCanvas.width = polygon.width * ratio hachingPatternSourceCanvas.height = polygon.height * ratio const ctx1 = hachingPatternSourceCanvas.getContext('2d') const gap = 10 ctx1.strokeStyle = 'green' // 선 색상 ctx1.lineWidth = 0.3 // 선 두께 for (let x = 0; x < hachingPatternSourceCanvas.width + hachingPatternSourceCanvas.height; x += gap) { ctx1.beginPath() ctx1.moveTo(x, 0) // 선 시작점 ctx1.lineTo(0, x) // 선 끝점 ctx1.stroke() } } const combinedPatternCanvas = document.createElement('canvas') combinedPatternCanvas.width = polygon.width * ratio combinedPatternCanvas.height = polygon.height * ratio const combinedCtx = combinedPatternCanvas.getContext('2d') // 첫 번째 패턴을 그린 후 두 번째 패턴을 덧입힘 combinedCtx.drawImage(patternSourceCanvas, 0, 0) combinedCtx.drawImage(hachingPatternSourceCanvas, 0, 0) // 패턴 생성 const pattern = new fabric.Pattern({ source: combinedPatternCanvas, repeat: 'repeat', }) polygon.set('fill', null) polygon.set('fill', pattern) polygon.roofMaterial = roofMaterial setPolygonLinesActualSize(polygon) changeCorridorDimensionText() polygon.canvas?.renderAll() } catch (e) { console.log(e) } } function removeRoofMaterial(roof = currentObject) { if (roof === null || roof.name !== POLYGON_TYPE.ROOF) { return } roof.set('fill', null) roof.roofMaterial = null canvas?.renderAll() } function removeAllRoofMaterial() { const roofBases = canvas.getObjects().filter((obj) => obj.name === POLYGON_TYPE.ROOF) roofBases.forEach((roofBase) => { removeRoofMaterial(roofBase) }) } function moveRoofMaterial(currentMousePos) { const roofBase = canvas .getObjects() .filter((obj) => obj.name === POLYGON_TYPE.ROOF) .filter((roof) => roof.inPolygon(currentMousePos)) if (roofBase.length === 0) { return } const roof = roofBase[0] const wall = canvas.getObjects().find((obj) => obj.name === POLYGON_TYPE.WALL && obj.attributes?.roofId === roof.id) const checkPolygon = new fabric.Polygon(roof.points, { name: 'moveRoofPolygon', stroke: '#ff0000', strokeWidth: 5, fill: 'transparent', selectable: false, originX: 'center', originY: 'center', }) canvas.add(checkPolygon) canvas.renderAll() canvas.on('mouse:move', (event) => { const mousePos = canvas.getPointer(event.e) checkPolygon.left = mousePos.x checkPolygon.top = mousePos.y canvas.renderAll() }) canvas.on('mouse:down', (event) => { let mousePos = canvas.getPointer(event.e) mousePos = { x: Math.round(mousePos.x), y: Math.round(mousePos.y) } const texts = canvas.getObjects().filter((obj) => obj.type === 'text' && (obj.attributes?.roofId === roof.id || obj.parentId === roof.id)) texts.forEach((text) => canvas.remove(text)) const allRoofObject = canvas .getObjects() .filter((obj) => obj !== roof && (obj.attributes?.roofId === roof.id || obj.parentId === roof.id || obj.parentId === wall.id)) /** 지붕이 움직인 만큼의 delta를 구한다. */ const originalRoofLeft = roof.left const originalRoofTop = roof.top roof.set({ left: mousePos.x, top: mousePos.y, originX: 'center', originY: 'center', objectCaching: false, }) roof.fire('polygonMoved') roof.fire('modified') // 기존 위치와 새로운 위치의 차이를 계산 const deltaX = roof.left - originalRoofLeft const deltaY = roof.top - originalRoofTop // Move all related objects by the delta allRoofObject.forEach((obj) => { if (obj.points !== undefined) { obj.set({ left: obj.left + deltaX, top: obj.top + deltaY, originX: 'center', originY: 'center', objectCaching: false, }) obj.fire('polygonMoved') obj.fire('modified') } else { obj.set({ left: obj.left + deltaX, top: obj.top + deltaY, x1: obj.x1 + deltaX, y1: obj.y1 + deltaY, x2: obj.x2 + deltaX, y2: obj.y2 + deltaY, objectCaching: false, }) if (obj.type === 'QLine') { obj.set({ startPoint: { x: obj.startPoint.x + deltaX, y: obj.startPoint.y + deltaY }, endPoint: { x: obj.endPoint.x + deltaX, y: obj.endPoint.y + deltaY }, }) } obj.setCoords() } if (obj.type === 'QLine') { obj.addLengthText() } if (obj.name === 'outerLine') { const { id } = obj const pitchText = canvas.getObjects().filter((obj) => obj.name === 'pitchText' && obj.parentId === id) canvas.remove(...pitchText) addPitchText(obj) } }) canvas.off('mouse:move') canvas.off('mouse:down') canvas.remove(checkPolygon) canvas.renderAll() }) } function convertAbsolutePoint(area) { return area.points.map((p) => fabric.util.transformPoint( { x: p.x - area.pathOffset.x, y: p.y - area.pathOffset.y, }, area.calcTransformMatrix(), ), ) } const removeOuterLines = (currentMousePos) => { const roofBase = canvas .getObjects() .filter((obj) => obj.name === POLYGON_TYPE.ROOF) .filter((roof) => roof.inPolygon(currentMousePos)) if (roofBase.length === 0) { return } const roof = roofBase[0] const wall = roof.wall canvas.remove(roof) canvas.remove(wall) const allRoofObject = canvas .getObjects() .filter( (obj) => /*obj !== roof && obj !== wall &&*/ obj.attributes?.roofId === roof.id || obj.parentId === roof.id || obj.parentId === wall?.id, ) const auxilaryObject = canvas.getObjects().filter((obj) => obj.name === 'auxiliaryLine' && !obj.isAuxiliaryFixed) allRoofObject.forEach((obj) => { canvas.remove(obj) }) auxilaryObject.forEach((obj) => { canvas.remove(obj) }) canvas.renderAll() resetPoints() } return { setSurfaceShapePattern, removeRoofMaterial, removeAllRoofMaterial, moveRoofMaterial, removeOuterLines } }