qcast-front/src/hooks/usePolygon.js

425 lines
12 KiB
JavaScript

import { canvasState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil'
import { fabric } from 'fabric'
import { getDirectionByPoint } from '@/util/canvas-util'
import { QPolygon } from '@/components/fabric/QPolygon'
import { isSamePoint } from '@/util/qpolygon-utils'
import { flowDisplaySelector } from '@/store/settingAtom'
export const usePolygon = () => {
const canvas = useRecoilValue(canvasState)
const isFlowDisplay = useRecoilValue(flowDisplaySelector)
const fontSize = useRecoilValue(fontSizeState)
const fontFamily = useRecoilValue(fontFamilyState)
const addPolygon = (points, options) => {
const polygon = new QPolygon(points, {
...options,
fill: options.fill || 'transparent',
stroke: options.stroke || '#000000',
fontSize: fontSize,
fontFamily: fontFamily,
selectable: true,
})
canvas?.add(polygon)
return polygon
}
const addPolygonByLines = (lines, options) => {
//lines의 idx를 정렬한다.
lines.sort((a, b) => a.idx - b.idx)
const points = createPolygonPointsFromOuterLines(lines)
return addPolygon(points, {
...options,
})
}
const addLengthText = (polygon) => {
const points = polygon.get('points')
points.forEach((start, i) => {
const end = points[(i + 1) % points.length]
const dx = end.x - start.x
const dy = end.y - start.y
const length = Number(Math.sqrt(dx * dx + dy * dy).toFixed(1)) * 10
const midPoint = new fabric.Point((start.x + end.x) / 2, (start.y + end.y) / 2)
const degree = (Math.atan2(dy, dx) * 180) / Math.PI
// Create new text object if it doesn't exist
const text = new fabric.Text(length.toString(), {
left: midPoint.x,
top: midPoint.y,
fontSize: fontSize,
fontFamily: fontFamily,
parentId: polygon.id,
minX: Math.min(start.x, end.x),
maxX: Math.max(start.x, end.x),
minY: Math.min(start.y, end.y),
maxY: Math.max(start.y, end.y),
parentDirection: getDirectionByPoint(start, end),
parentDegree: degree,
dirty: true,
editable: true,
selectable: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
idx: i,
name: 'lengthText',
parent: this,
})
// this.texts.push(text)
canvas.add(text)
})
canvas.renderAll()
}
const createPolygonPointsFromOuterLines = (outerLines) => {
if (!outerLines || outerLines.length === 0) {
return []
}
// Extract points from outerLines
return outerLines.map((line) => ({
x: line.x1,
y: line.y1,
}))
}
const removePolygon = (polygon) => {
const texts = canvas.getObjects().filter((obj) => obj.parentId === polygon.id)
texts.forEach((text) => {
canvas.remove(text)
})
canvas.remove(polygon)
canvas.renderAll()
}
/**
* poolygon의 방향에 따라 화살표를 추가한다.
* @param polygon
*/
const drawDirectionArrow = (polygon) => {
const direction = polygon.direction
if (!direction) {
return
}
polygon.canvas
.getObjects()
.filter((obj) => obj.name === 'directionText' && obj.parent === polygon.arrow)
.forEach((obj) => polygon.canvas.remove(obj))
let arrow = null
let points = []
if (polygon.arrow) {
polygon.canvas.remove(polygon.arrow)
}
let centerPoint = { x: polygon.left, y: polygon.top }
let stickeyPoint
const polygonMaxX = Math.max(...polygon.getCurrentPoints().map((point) => point.x))
const polygonMinX = Math.min(...polygon.getCurrentPoints().map((point) => point.x))
const polygonMaxY = Math.max(...polygon.getCurrentPoints().map((point) => point.y))
const polygonMinY = Math.min(...polygon.getCurrentPoints().map((point) => point.y))
switch (direction) {
case 'north':
points = [
{ x: centerPoint.x, y: polygonMinY - 50 },
{ x: centerPoint.x + 20, y: polygonMinY - 50 },
{ x: centerPoint.x + 20, y: polygonMinY - 80 },
{ x: centerPoint.x + 50, y: polygonMinY - 80 },
{ x: centerPoint.x, y: polygonMinY - 110 },
{ x: centerPoint.x - 50, y: polygonMinY - 80 },
{ x: centerPoint.x - 20, y: polygonMinY - 80 },
{ x: centerPoint.x - 20, y: polygonMinY - 50 },
]
stickeyPoint = { x: centerPoint.x, y: polygonMinY - 110 }
break
case 'south':
points = [
{ x: centerPoint.x, y: polygonMaxY + 50 },
{ x: centerPoint.x + 20, y: polygonMaxY + 50 },
{ x: centerPoint.x + 20, y: polygonMaxY + 80 },
{ x: centerPoint.x + 50, y: polygonMaxY + 80 },
{ x: centerPoint.x, y: polygonMaxY + 110 },
{ x: centerPoint.x - 50, y: polygonMaxY + 80 },
{ x: centerPoint.x - 20, y: polygonMaxY + 80 },
{ x: centerPoint.x - 20, y: polygonMaxY + 50 },
]
stickeyPoint = { x: centerPoint.x, y: polygonMaxY + 110 }
break
case 'west':
points = [
{ x: polygonMinX - 50, y: centerPoint.y },
{ x: polygonMinX - 50, y: centerPoint.y + 20 },
{ x: polygonMinX - 80, y: centerPoint.y + 20 },
{ x: polygonMinX - 80, y: centerPoint.y + 50 },
{ x: polygonMinX - 110, y: centerPoint.y },
{ x: polygonMinX - 80, y: centerPoint.y - 50 },
{ x: polygonMinX - 80, y: centerPoint.y - 20 },
{ x: polygonMinX - 50, y: centerPoint.y - 20 },
]
stickeyPoint = { x: polygonMinX - 110, y: centerPoint.y }
break
case 'east':
points = [
{ x: polygonMaxX + 50, y: centerPoint.y },
{ x: polygonMaxX + 50, y: centerPoint.y + 20 },
{ x: polygonMaxX + 80, y: centerPoint.y + 20 },
{ x: polygonMaxX + 80, y: centerPoint.y + 50 },
{ x: polygonMaxX + 110, y: centerPoint.y },
{ x: polygonMaxX + 80, y: centerPoint.y - 50 },
{ x: polygonMaxX + 80, y: centerPoint.y - 20 },
{ x: polygonMaxX + 50, y: centerPoint.y - 20 },
]
stickeyPoint = { x: polygonMaxX + 110, y: centerPoint.y }
break
}
arrow = new fabric.Polygon(points, {
selectable: false,
name: 'arrow',
fill: 'transparent',
stroke: 'black',
direction: direction,
parent: polygon,
stickeyPoint: stickeyPoint,
visible: isFlowDisplay,
})
polygon.arrow = arrow
polygon.canvas.add(arrow)
polygon.canvas.renderAll()
drawDirectionStringToArrow(polygon.canvas, 0)
}
/**
* 방향을 나타낸 화살표에 각도에 따라 글씨 추가
* @param canvas
* @param compass
*/
const drawDirectionStringToArrow = (canvas, compass = 0) => {
const arrows = canvas?.getObjects().filter((obj) => obj.name === 'arrow')
if (arrows.length === 0) {
return
}
const eastArrows = arrows.filter((arrow) => arrow.direction === 'east')
const westArrows = arrows.filter((arrow) => arrow.direction === 'west')
const northArrows = arrows.filter((arrow) => arrow.direction === 'north')
const southArrows = arrows.filter((arrow) => arrow.direction === 'south')
let southText = '南'
let eastText = '東'
let westText = '西'
let northText = '北'
if (compass === 0 || compass === 360) {
// 남,동,서 가능
// 그대로
} else if (compass < 45) {
//남(남남동),동(동북동),서(서남서) 가능
//북(북북서)
southText = '南南東'
eastText = '東北東'
westText = '西南西'
northText = '北北西'
} else if (compass === 45) {
// 남, 서 가능
// 남(남동)
// 서(남서)
// 북(북서)
// 동(북동)
southText = '南東'
westText = '南西'
northText = '北西'
eastText = '北東'
} else if (compass < 90) {
// 북(서북서)
// 동 (북북동)
// 남(동남동)
// 서(남남서)
northText = '北西北'
eastText = '北北東'
southText = '東南東'
westText = '南南西'
} else if (compass === 90) {
// 동(북)
// 서(남)
// 남(동)
// 북(서)
eastText = '北'
westText = '南'
southText = '東'
northText = '西'
} else if (compass < 135) {
// 남,서,북 가능
// 동(북북서)
// 서(남남동)
// 남(동북동)
// 북(서남서)
eastText = '北北西'
westText = '南南東'
southText = '東北東'
northText = '西南西'
} else if (compass === 135) {
// 서,북 가능
// 서(남동)
// 북(남서)
// 남(북동)
// 동(북서)
westText = '南東'
northText = '南西'
southText = '北東'
eastText = '北西'
} else if (compass < 180) {
// 북,동,서 가능
// 북(남남서)
// 동(서북서)
// 남(북북동)
// 서(동남동)
northText = '南南西'
eastText = '西北西'
southText = '北北東'
westText = '東南東'
} else if (compass === 180) {
// 북,동,서 가능
// 북(남)
// 동(서)
// 남(북)
// 서(동)
northText = '南'
eastText = '西'
southText = '北'
westText = '東'
} else if (compass < 225) {
// 서,북,동 가능
// 북(남남동)
// 동(서남서)
// 남(북북서)
// 서(동남동)
northText = '南南東'
eastText = '西南西'
southText = '北北西'
westText = '東南東'
} else if (compass === 225) {
// 북,동 가능
// 북(남동)
// 동(남서)
// 남(북서)
// 서(북동)
northText = '南東'
eastText = '南西'
southText = '北西'
westText = '北東'
} else if (compass < 270) {
// 북동남 가능
// 북(동남동)
// 동(남남서)
// 남(서북서)
// 서(북북동)
northText = '東南東'
eastText = '南南西'
southText = '西北西'
westText = '北北東'
} else if (compass === 270) {
// 북동남 가능
// 북(동)
// 동(남)
// 남(서)
// 서(북)
northText = '東'
eastText = '南'
southText = '西'
westText = '北'
} else if (compass < 315) {
// 북,동,남 가능
// 북(동북동)
// 동(남남동)
// 남(서남서)
// 서(북북서)
northText = '東北東'
eastText = '南南東'
southText = '西南西'
westText = '北北西'
} else if (compass === 315) {
// 동,남 가능
// 북(북동)
// 동(남동)
// 남(남서)
// 서(북서)
northText = '北東'
eastText = '南東'
southText = '南西'
westText = '北西'
} else if (compass < 360) {
// 남,동,서 가능
// 북(북북동)
// 동(동남동)
// 남(남남서)
// 서(서북서)
northText = '北北東'
eastText = '東南東'
southText = '南南西'
westText = '西北西'
}
clearDirectionText(canvas)
addTextByArrows(eastArrows, eastText, canvas)
addTextByArrows(westArrows, westText, canvas)
addTextByArrows(northArrows, northText, canvas)
addTextByArrows(southArrows, southText, canvas)
}
const clearDirectionText = (canvas) => {
const texts = canvas.getObjects().filter((obj) => obj.name === 'directionText')
texts.forEach((text) => {
canvas.remove(text)
})
}
const addTextByArrows = (arrows, txt, canvas) => {
arrows.forEach((arrow, index) => {
const text = new fabric.Text(`${txt}${index + 1}`, {
fontSize: arrow.parent.fontSize,
fill: 'black',
originX: 'center',
originY: 'center',
name: 'directionText',
selectable: false,
left: arrow.stickeyPoint.x,
top: arrow.stickeyPoint.y,
parent: arrow,
visible: isFlowDisplay,
})
canvas.add(text)
})
}
return {
addPolygon,
addPolygonByLines,
removePolygon,
drawDirectionArrow,
}
}