365 lines
12 KiB
JavaScript
365 lines
12 KiB
JavaScript
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 }
|
|
}
|