splitPolygonWithLines usePolygon으로 수정

방향 index 제거
This commit is contained in:
hyojun.choi 2024-11-07 16:49:05 +09:00
parent 1cdd5432f5
commit dcd364bca0
5 changed files with 338 additions and 256 deletions

View File

@ -159,6 +159,8 @@ export const SAVE_KEY = [
'groupName',
'lineDirection',
'groupId',
'planeSize',
'actualSize',
]
export const OBJECT_PROTOTYPE = [fabric.Line.prototype, fabric.Polygon.prototype, fabric.Triangle.prototype]

View File

@ -2,7 +2,6 @@ import { useRecoilValue } from 'recoil'
import { canvasState } from '@/store/canvasAtom'
import { useEffect, useState } from 'react'
import { setSurfaceShapePattern } from '@/util/canvas-util'
import { splitPolygonWithLines } from '@/util/qpolygon-utils'
import { useSwal } from '@/hooks/useSwal'
import { usePolygon } from '@/hooks/usePolygon'
import { roofDisplaySelector } from '@/store/settingAtom'
@ -13,7 +12,7 @@ import { POLYGON_TYPE } from '@/common/common'
export function useRoofAllocationSetting(id) {
const canvas = useRecoilValue(canvasState)
const roofDisplay = useRecoilValue(roofDisplaySelector)
const { drawDirectionArrow } = usePolygon()
const { drawDirectionArrow, addLengthText, splitPolygonWithLines } = usePolygon()
const { closePopup } = usePopup()
const { swalFire } = useSwal()

View File

@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'
import { useRecoilState, useRecoilValue } from 'recoil'
import { v4 as uuidv4 } from 'uuid'
import { canvasSizeState, canvasState, canvasZoomState, currentObjectState, fontFamilyState, fontSizeState } from '@/store/canvasAtom'
import { canvasSizeState, canvasState, canvasZoomState, currentObjectState } from '@/store/canvasAtom'
import { QPolygon } from '@/components/fabric/QPolygon'
import { usePlan } from '@/hooks/usePlan'
import { fontSelector } from '@/store/fontAtom'
@ -12,8 +12,6 @@ export function useCanvasEvent() {
const [canvasForEvent, setCanvasForEvent] = useState(null)
const [currentObject, setCurrentObject] = useRecoilState(currentObjectState)
const canvasSize = useRecoilValue(canvasSizeState)
const fontSize = useRecoilValue(fontSizeState)
const fontFamily = useRecoilValue(fontFamilyState)
const [canvasZoom, setCanvasZoom] = useRecoilState(canvasZoomState)
const lengthTextOption = useRecoilValue(fontSelector('lengthText'))
const { modifiedPlanFlag, setModifiedPlanFlag } = usePlan()
@ -211,7 +209,7 @@ export function useCanvasEvent() {
setCurrentObject(target)
const { selected } = e
if (selected.length > 0) {
if (selected?.length > 0) {
selected.forEach((obj) => {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'red' })
@ -224,7 +222,7 @@ export function useCanvasEvent() {
setCurrentObject(null)
const { deselected } = e
if (deselected.length > 0) {
if (deselected?.length > 0) {
deselected.forEach((obj) => {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'black' })
@ -238,7 +236,7 @@ export function useCanvasEvent() {
setCurrentObject(target)
const { selected, deselected } = e
if (deselected.length > 0) {
if (deselected?.length > 0) {
deselected.forEach((obj) => {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'black' })
@ -246,7 +244,7 @@ export function useCanvasEvent() {
})
}
if (selected.length > 0) {
if (selected?.length > 0) {
selected.forEach((obj) => {
if (obj.type === 'QPolygon') {
obj.set({ stroke: 'red' })

View File

@ -1,11 +1,13 @@
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, fontFamilyState, fontSizeState, pitchTextSelector } from '@/store/canvasAtom'
import { useRecoilValue } from 'recoil'
import { fabric } from 'fabric'
import { getDegreeByChon, getDirectionByPoint } from '@/util/canvas-util'
import { getDegreeByChon, getDirectionByPoint, isPointOnLine } from '@/util/canvas-util'
import { QPolygon } from '@/components/fabric/QPolygon'
import { isSamePoint } from '@/util/qpolygon-utils'
import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils'
import { flowDisplaySelector } from '@/store/settingAtom'
import { fontSelector } from '@/store/fontAtom'
import { QLine } from '@/components/fabric/QLine'
import { POLYGON_TYPE } from '@/common/common'
export const usePolygon = () => {
const canvas = useRecoilValue(canvasState)
@ -25,6 +27,7 @@ export const usePolygon = () => {
})
canvas?.add(polygon)
addLengthText(polygon)
return polygon
}
@ -40,7 +43,64 @@ export const usePolygon = () => {
}
const addLengthText = (polygon) => {
const points = polygon.get('points')
const lengthTexts = canvas.getObjects().filter((obj) => obj.name === 'lengthText' && obj.parentId === polygon.id)
lengthTexts.forEach((text) => {
canvas.remove(text)
})
const lines = polygon.lines
lines.forEach((line, i) => {
const length = line.getLength()
const { planeSize, actualSize } = line.attributes
const scaleX = line.scaleX
const scaleY = line.scaleY
const x1 = line.left
const y1 = line.top
const x2 = line.left + line.width * scaleX
const y2 = line.top + line.height * scaleY
let left, top
if (line.direction === 'left' || line.direction === 'right') {
left = (x1 + x2) / 2
top = (y1 + y2) / 2 + 10
} else if (line.direction === 'top' || line.direction === 'bottom') {
left = (x1 + x2) / 2 + 10
top = (y1 + y2) / 2
}
const minX = line.left
const maxX = line.left + line.width
const minY = line.top
const maxY = line.top + line.length
const degree = (Math.atan2(y2 - y1, x2 - x1) * 180) / Math.PI
const text = new fabric.Textbox(actualSize ? actualSize.toString() : planeSize ? planeSize.toString() : length.toString(), {
left: left,
top: top,
fontSize: lengthTextFontOptions.fontSize.value,
minX,
maxX,
minY,
maxY,
parentDirection: line.direction,
parentDegree: degree,
parentId: polygon.id,
planeSize,
actualSize,
editable: false,
selectable: true,
lockRotation: true,
lockScalingX: true,
lockScalingY: true,
parent: polygon,
name: 'lengthText',
})
canvas.add(text)
})
/*const points = polygon.get('points')
points.forEach((start, i) => {
const end = points[(i + 1) % points.length]
const dx = end.x - start.x
@ -71,12 +131,12 @@ export const usePolygon = () => {
lockScalingY: true,
idx: i,
name: 'lengthText',
parent: this,
parent: polygon,
})
// this.texts.push(text)
canvas.add(text)
})
})*/
canvas.renderAll()
}
@ -409,7 +469,8 @@ export const usePolygon = () => {
const addTextByArrows = (arrows, txt, canvas) => {
arrows.forEach((arrow, index) => {
const textStr = `${txt}${index + 1} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`
// const textStr = `${txt}${index + 1} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`
const textStr = `${txt} (${currentAngleType === ANGLE_TYPE.SLOPE ? arrow.pitch : getDegreeByChon(arrow.pitch)}${pitchText})`
const text = new fabric.Text(`${textStr}`, {
fontSize: flowFontOptions.fontSize.value,
@ -432,10 +493,272 @@ export const usePolygon = () => {
})
}
const splitPolygonWithLines = (polygon) => {
polygon.set({ visible: false })
let innerLines = [...polygon.innerLines]
let polygonLines = [...polygon.lines]
const roofs = []
let delIndexs = []
let newLines = []
polygonLines.forEach((line, index) => {
line.tempIndex = index
innerLines.forEach((innerLine) => {
let newLine1, newLine2
if (isPointOnLine(line, innerLine.startPoint)) {
// 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다.
newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], {
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], {
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
delIndexs.push(polygonLines.indexOf(line))
canvas.remove(polygonLines[polygonLines.indexOf(line)])
if (newLine1.length / 10 > 10) {
newLines.push(newLine1)
}
if (newLine2.length / 10 > 10) {
newLines.push(newLine2)
}
}
if (isPointOnLine(line, innerLine.endPoint)) {
newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], {
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], {
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
delIndexs.push(polygonLines.indexOf(line))
canvas.remove(polygonLines[polygonLines.indexOf(line)])
if (newLine1.length / 10 > 10) {
newLines.push(newLine1)
}
if (newLine2.length / 10 > 10) {
newLines.push(newLine2)
}
}
})
})
polygonLines = polygonLines.filter((line) => !delIndexs.includes(line.tempIndex))
polygonLines = [...polygonLines, ...newLines]
const allLines = [...polygonLines, ...innerLines]
/**
* 왼쪽 상단을 startPoint로 전부 변경
*/
allLines.forEach((line) => {
let startPoint // 시작점
let endPoint // 끝점
if (line.x1 < line.x2) {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
} else if (line.x1 > line.x2) {
startPoint = { x: line.x2, y: line.y2 }
endPoint = { x: line.x1, y: line.y1 }
} else {
if (line.y1 < line.y2) {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
} else {
startPoint = { x: line.x2, y: line.y2 }
endPoint = { x: line.x1, y: line.y1 }
}
}
line.startPoint = startPoint
line.endPoint = endPoint
})
polygonLines.forEach((line) => {
const startPoint = line.startPoint // 시작점
let arrivalPoint = line.endPoint // 도착점
let currentPoint = startPoint
const roofPoints = [startPoint]
const startLine = line
const visitPoints = [startPoint]
const visitLines = [startLine]
let cnt = 0
while (!isSamePoint(currentPoint, arrivalPoint)) {
let nextLines = allLines.filter(
(line2) =>
(isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
line !== line2 &&
innerLines.includes(line2) &&
!visitLines.includes(line2),
)
if (nextLines.length === 0) {
nextLines = allLines.filter(
(line2) =>
(isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
line !== line2 &&
!visitLines.includes(line2),
)
}
if (!nextLines) {
break
}
let comparisonPoints = []
nextLines.forEach((nextLine) => {
if (isSamePoint(nextLine.startPoint, currentPoint)) {
comparisonPoints.push(nextLine.endPoint)
} else {
comparisonPoints.push(nextLine.startPoint)
}
})
comparisonPoints = comparisonPoints.filter((point) => !visitPoints.some((visitPoint) => isSamePoint(visitPoint, point)))
comparisonPoints = comparisonPoints.filter((point) => !isSamePoint(point, currentPoint))
const minDistancePoint = comparisonPoints.reduce((prev, current) => {
const prevDistance = Math.sqrt(Math.pow(prev.x - arrivalPoint.x, 2) + Math.pow(prev.y - arrivalPoint.y, 2))
const currentDistance = Math.sqrt(Math.pow(current.x - arrivalPoint.x, 2) + Math.pow(current.y - arrivalPoint.y, 2))
return prevDistance < currentDistance ? prev : current
}, comparisonPoints[0])
nextLines.forEach((nextLine) => {
if (isSamePoint(nextLine.startPoint, minDistancePoint) || isSamePoint(nextLine.endPoint, minDistancePoint)) {
visitLines.push(nextLine)
}
})
currentPoint = { ...minDistancePoint }
roofPoints.push(currentPoint)
cnt++
if (cnt > 100) {
throw new Error('무한루프')
}
}
roofs.push(roofPoints)
})
const newRoofs = removeDuplicatePolygons(roofs)
newRoofs.forEach((roofPoint, index) => {
let defense, pitch
const polygonLines = [...polygon.lines]
let representLines = []
let representLine
// 지붕을 그리면서 기존 polygon의 line중 연결된 line을 찾는다.
polygonLines.forEach((line) => {
let startFlag = false
let endFlag = false
const startPoint = line.startPoint
const endPoint = line.endPoint
roofPoint.forEach((point, index) => {
if (isSamePoint(point, startPoint)) {
startFlag = true
}
if (isSamePoint(point, endPoint)) {
endFlag = true
}
})
if (startFlag && endFlag) {
if (!representLines.includes(line)) {
representLines.push(line)
}
}
})
// representLines중 가장 긴 line을 찾는다.
representLines.forEach((line) => {
if (!representLine) {
representLine = line
} else {
if (representLine.length < line.length) {
representLine = line
}
}
})
const direction = newRoofs.length === 1 ? polygon.direction : representLine.direction
const polygonDirection = polygon.direction
switch (direction) {
case 'top':
defense = 'east'
break
case 'right':
defense = 'south'
break
case 'bottom':
defense = 'west'
break
case 'left':
defense = 'north'
break
}
pitch = polygon.lines[index].attributes?.pitch ?? 0
const roof = new QPolygon(roofPoint, {
fontSize: polygon.fontSize,
stroke: 'black',
fill: 'transparent',
strokeWidth: 3,
name: POLYGON_TYPE.ROOF,
originX: 'center',
originY: 'center',
selectable: true,
defense: defense,
direction: newRoofs.length === 1 ? polygonDirection : defense,
pitch: pitch,
})
//allLines중 생성된 roof와 관련있는 line을 찾는다.
roof.lines = [...polygon.lines, ...polygon.innerLines].filter((line) => {
let startFlag = false
let endFlag = false
const startPoint = line.startPoint
const endPoint = line.endPoint
roofPoint.forEach((point, index) => {
if (isSamePoint(point, startPoint)) {
startFlag = true
}
if (isSamePoint(point, endPoint)) {
endFlag = true
}
})
return startFlag && endFlag
})
canvas.add(roof)
addLengthText(roof)
canvas.remove(polygon)
canvas.renderAll()
})
}
return {
addPolygon,
addPolygonByLines,
removePolygon,
drawDirectionArrow,
addLengthText,
splitPolygonWithLines,
}
}

View File

@ -1177,246 +1177,6 @@ export default function offsetPolygon(vertices, offset) {
polygon.canvas.renderAll()
})
}*/
export const splitPolygonWithLines = (polygon) => {
const canvas = polygon.canvas
polygon.set({ visible: false })
let innerLines = [...polygon.innerLines]
let polygonLines = [...polygon.lines]
const roofs = []
let delIndexs = []
let newLines = []
polygonLines.forEach((line, index) => {
line.tempIndex = index
innerLines.forEach((innerLine) => {
let newLine1, newLine2
if (isPointOnLine(line, innerLine.startPoint)) {
// 해당 line을 startPoint로 나눈 line2개를 canvas에 추가 하고 기존 line을 제거한다.
newLine1 = new QLine([line.x1, line.y1, innerLine.startPoint.x, innerLine.startPoint.y], {
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
newLine2 = new QLine([innerLine.startPoint.x, innerLine.startPoint.y, line.x2, line.y2], {
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
delIndexs.push(polygonLines.indexOf(line))
canvas.remove(polygonLines[polygonLines.indexOf(line)])
if (newLine1.length / 10 > 10) {
newLines.push(newLine1)
}
if (newLine2.length / 10 > 10) {
newLines.push(newLine2)
}
}
if (isPointOnLine(line, innerLine.endPoint)) {
newLine1 = new QLine([line.x1, line.y1, innerLine.endPoint.x, innerLine.endPoint.y], {
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
newLine2 = new QLine([innerLine.endPoint.x, innerLine.endPoint.y, line.x2, line.y2], {
fontSize: polygon.fontSize,
stroke: 'black',
strokeWidth: 3,
})
delIndexs.push(polygonLines.indexOf(line))
canvas.remove(polygonLines[polygonLines.indexOf(line)])
if (newLine1.length / 10 > 10) {
newLines.push(newLine1)
}
if (newLine2.length / 10 > 10) {
newLines.push(newLine2)
}
}
})
})
polygonLines = polygonLines.filter((line) => !delIndexs.includes(line.tempIndex))
polygonLines = [...polygonLines, ...newLines]
const allLines = [...polygonLines, ...innerLines]
/**
* 왼쪽 상단을 startPoint로 전부 변경
*/
allLines.forEach((line) => {
let startPoint // 시작점
let endPoint // 끝점
if (line.x1 < line.x2) {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
} else if (line.x1 > line.x2) {
startPoint = { x: line.x2, y: line.y2 }
endPoint = { x: line.x1, y: line.y1 }
} else {
if (line.y1 < line.y2) {
startPoint = { x: line.x1, y: line.y1 }
endPoint = { x: line.x2, y: line.y2 }
} else {
startPoint = { x: line.x2, y: line.y2 }
endPoint = { x: line.x1, y: line.y1 }
}
}
line.startPoint = startPoint
line.endPoint = endPoint
})
polygonLines.forEach((line) => {
const startPoint = line.startPoint // 시작점
let arrivalPoint = line.endPoint // 도착점
let currentPoint = startPoint
const roofPoints = [startPoint]
const startLine = line
const visitPoints = [startPoint]
const visitLines = [startLine]
let cnt = 0
while (!isSamePoint(currentPoint, arrivalPoint)) {
line.set({ stroke: 'red' })
canvas.renderAll()
let nextLines = allLines.filter(
(line2) =>
(isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
line !== line2 &&
innerLines.includes(line2) &&
!visitLines.includes(line2),
)
if (nextLines.length === 0) {
nextLines = allLines.filter(
(line2) =>
(isSamePoint(line2.startPoint, currentPoint) || isSamePoint(line2.endPoint, currentPoint)) &&
line !== line2 &&
!visitLines.includes(line2),
)
}
if (!nextLines) {
break
}
let comparisonPoints = []
nextLines.forEach((nextLine) => {
if (isSamePoint(nextLine.startPoint, currentPoint)) {
comparisonPoints.push(nextLine.endPoint)
} else {
comparisonPoints.push(nextLine.startPoint)
}
})
comparisonPoints = comparisonPoints.filter((point) => !visitPoints.some((visitPoint) => isSamePoint(visitPoint, point)))
comparisonPoints = comparisonPoints.filter((point) => !isSamePoint(point, currentPoint))
const minDistancePoint = comparisonPoints.reduce((prev, current) => {
const prevDistance = Math.sqrt(Math.pow(prev.x - arrivalPoint.x, 2) + Math.pow(prev.y - arrivalPoint.y, 2))
const currentDistance = Math.sqrt(Math.pow(current.x - arrivalPoint.x, 2) + Math.pow(current.y - arrivalPoint.y, 2))
return prevDistance < currentDistance ? prev : current
}, comparisonPoints[0])
nextLines.forEach((nextLine) => {
if (isSamePoint(nextLine.startPoint, minDistancePoint) || isSamePoint(nextLine.endPoint, minDistancePoint)) {
visitLines.push(nextLine)
}
})
currentPoint = { ...minDistancePoint }
roofPoints.push(currentPoint)
cnt++
if (cnt > 100) {
throw new Error('무한루프')
}
}
roofs.push(roofPoints)
})
const newRoofs = removeDuplicatePolygons(roofs)
newRoofs.forEach((roofPoint, index) => {
let defense, pitch
const polygonLines = [...polygon.lines]
let representLines = []
let representLine
// 지붕을 그리면서 기존 polygon의 line중 연결된 line을 찾는다.
polygonLines.forEach((line) => {
let startFlag = false
let endFlag = false
const startPoint = line.startPoint
const endPoint = line.endPoint
roofPoint.forEach((point, index) => {
if (isSamePoint(point, startPoint)) {
startFlag = true
}
if (isSamePoint(point, endPoint)) {
endFlag = true
}
})
if (startFlag && endFlag) {
representLines.push(line)
}
})
// representLines중 가장 긴 line을 찾는다.
representLines.forEach((line) => {
if (!representLine) {
representLine = line
} else {
if (representLine.length < line.length) {
representLine = line
}
}
})
const direction = newRoofs.length === 1 ? polygon.direction : representLine.direction
const polygonDirection = polygon.direction
switch (direction) {
case 'top':
defense = 'east'
break
case 'right':
defense = 'south'
break
case 'bottom':
defense = 'west'
break
case 'left':
defense = 'north'
break
}
pitch = polygon.lines[index].attributes?.pitch ?? 0
const roof = new QPolygon(roofPoint, {
fontSize: polygon.fontSize,
stroke: 'black',
fill: 'transparent',
strokeWidth: 3,
name: POLYGON_TYPE.ROOF,
originX: 'center',
originY: 'center',
selectable: true,
defense: defense,
direction: newRoofs.length === 1 ? polygonDirection : defense,
pitch: pitch,
})
polygon.canvas.add(roof)
canvas.remove(polygon)
polygon.canvas.renderAll()
})
}
function normalizePoint(point) {
return {
@ -1434,7 +1194,7 @@ function arePolygonsEqual(polygon1, polygon2) {
return normalizedPolygon1.every((point, index) => arePointsEqual(point, normalizedPolygon2[index]))
}
function removeDuplicatePolygons(polygons) {
export function removeDuplicatePolygons(polygons) {
const uniquePolygons = []
polygons.forEach((polygon) => {