최단거리 roof 나누기 추가
This commit is contained in:
parent
46f46d734a
commit
5f648632dd
@ -1,7 +1,7 @@
|
|||||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
||||||
import { useRecoilValue } from 'recoil'
|
import { useRecoilValue } from 'recoil'
|
||||||
import { fabric } from 'fabric'
|
import { fabric } from 'fabric'
|
||||||
import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, isPointOnLine } from '@/util/canvas-util'
|
import { findAndRemoveClosestPoint, getDegreeByChon, getDegreeInOrientation, isPointOnLine, toFixedWithoutRounding } from '@/util/canvas-util'
|
||||||
import { QPolygon } from '@/components/fabric/QPolygon'
|
import { QPolygon } from '@/components/fabric/QPolygon'
|
||||||
import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils'
|
import { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils'
|
||||||
import { flowDisplaySelector } from '@/store/settingAtom'
|
import { flowDisplaySelector } from '@/store/settingAtom'
|
||||||
@ -757,7 +757,7 @@ export const usePolygon = () => {
|
|||||||
polygon.set({ visible: false })
|
polygon.set({ visible: false })
|
||||||
let innerLines = [...polygon.innerLines]
|
let innerLines = [...polygon.innerLines]
|
||||||
|
|
||||||
// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다.
|
/*// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다.
|
||||||
if (!innerLines || innerLines.length === 0) {
|
if (!innerLines || innerLines.length === 0) {
|
||||||
let innerLineTypes = Object.keys(LINE_TYPE.SUBLINE).map((key, value) => LINE_TYPE.SUBLINE[key])
|
let innerLineTypes = Object.keys(LINE_TYPE.SUBLINE).map((key, value) => LINE_TYPE.SUBLINE[key])
|
||||||
polygon.innerLines = canvas
|
polygon.innerLines = canvas
|
||||||
@ -771,8 +771,7 @@ export const usePolygon = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
innerLines = [...polygon.innerLines]
|
innerLines = [...polygon.innerLines]
|
||||||
}
|
}*/
|
||||||
|
|
||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
let polygonLines = [...polygon.lines]
|
let polygonLines = [...polygon.lines]
|
||||||
const roofs = []
|
const roofs = []
|
||||||
@ -830,8 +829,14 @@ export const usePolygon = () => {
|
|||||||
canvas.renderAll()
|
canvas.renderAll()
|
||||||
|
|
||||||
polygonLines.forEach((line) => {
|
polygonLines.forEach((line) => {
|
||||||
|
/*const originStroke = line.stroke
|
||||||
|
line.set({ stroke: 'red' })
|
||||||
|
canvas.renderAll()*/
|
||||||
const intersections = []
|
const intersections = []
|
||||||
innerLines.forEach((innerLine) => {
|
innerLines.forEach((innerLine) => {
|
||||||
|
/*const originInnerStroke = innerLine.stroke
|
||||||
|
innerLine.set({ stroke: 'red' })
|
||||||
|
canvas.renderAll()*/
|
||||||
if (isPointOnLine(line, innerLine.startPoint)) {
|
if (isPointOnLine(line, innerLine.startPoint)) {
|
||||||
if (isSamePoint(line.startPoint, innerLine.startPoint) || isSamePoint(line.endPoint, innerLine.startPoint)) {
|
if (isSamePoint(line.startPoint, innerLine.startPoint) || isSamePoint(line.endPoint, innerLine.startPoint)) {
|
||||||
return
|
return
|
||||||
@ -844,8 +849,13 @@ export const usePolygon = () => {
|
|||||||
}
|
}
|
||||||
intersections.push(innerLine.endPoint)
|
intersections.push(innerLine.endPoint)
|
||||||
}
|
}
|
||||||
|
/*innerLine.set({ stroke: originInnerStroke })
|
||||||
|
canvas.renderAll()*/
|
||||||
})
|
})
|
||||||
line.set({ intersections })
|
line.set({ intersections })
|
||||||
|
|
||||||
|
/*line.set({ stroke: originStroke })
|
||||||
|
canvas.renderAll()*/
|
||||||
})
|
})
|
||||||
|
|
||||||
const divideLines = polygonLines.filter((line) => line.intersections.length > 0)
|
const divideLines = polygonLines.filter((line) => line.intersections.length > 0)
|
||||||
@ -924,10 +934,52 @@ export const usePolygon = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
//polygonLines에서 divideLines를 제거하고 newLines를 추가한다.
|
//polygonLines에서 divideLines를 제거하고 newLines를 추가한다.
|
||||||
polygonLines = polygonLines.filter((line) => !divideLines.includes(line))
|
polygonLines = polygonLines.filter((line) => line.intersections?.length === 0)
|
||||||
|
|
||||||
polygonLines = [...polygonLines, ...newLines]
|
polygonLines = [...polygonLines, ...newLines]
|
||||||
|
|
||||||
const allLines = [...polygonLines, ...innerLines]
|
let allLines = [...polygonLines, ...innerLines]
|
||||||
|
|
||||||
|
/*allLines.forEach((line) => {
|
||||||
|
const originColor = line.stroke
|
||||||
|
|
||||||
|
line.set('stroke', 'red')
|
||||||
|
canvas.renderAll()
|
||||||
|
|
||||||
|
line.set('stroke', originColor)
|
||||||
|
canvas.renderAll()
|
||||||
|
})*/
|
||||||
|
|
||||||
|
const allPoints = []
|
||||||
|
|
||||||
|
// test용 좌표
|
||||||
|
const polygonLinesPoints = polygonLines.map((line) => {
|
||||||
|
return { startPoint: line.startPoint, endPoint: line.endPoint }
|
||||||
|
})
|
||||||
|
|
||||||
|
const innerLinesPoints = innerLines.map((line) => {
|
||||||
|
return { startPoint: line.startPoint, endPoint: line.endPoint }
|
||||||
|
})
|
||||||
|
|
||||||
|
polygonLinesPoints.forEach(({ startPoint, endPoint }) => {
|
||||||
|
allPoints.push(startPoint)
|
||||||
|
allPoints.push(endPoint)
|
||||||
|
})
|
||||||
|
|
||||||
|
innerLinesPoints.forEach(({ startPoint, endPoint }) => {
|
||||||
|
allPoints.push(startPoint)
|
||||||
|
allPoints.push(endPoint)
|
||||||
|
})
|
||||||
|
|
||||||
|
// allPoints에서 중복을 제거한다.
|
||||||
|
const uniquePoints = allPoints.filter((point, index, self) => {
|
||||||
|
return (
|
||||||
|
index ===
|
||||||
|
self.findIndex((p) => {
|
||||||
|
return isSamePoint(p, point)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
// 2025-02-19 대각선은 케라바, 직선은 용마루로 세팅
|
// 2025-02-19 대각선은 케라바, 직선은 용마루로 세팅
|
||||||
innerLines.forEach((innerLine) => {
|
innerLines.forEach((innerLine) => {
|
||||||
@ -978,102 +1030,23 @@ export const usePolygon = () => {
|
|||||||
line.endPoint = endPoint
|
line.endPoint = endPoint
|
||||||
})
|
})
|
||||||
|
|
||||||
// polygon line에서 각각 출발한다.
|
// polygonLines에서 시작점 혹은 끝점이 innerLines와 연결된 line만 가져온다.
|
||||||
polygonLines.forEach((line) => {
|
let startLines = polygonLines.filter((line) => {
|
||||||
/*line.set({ strokeWidth: 5, stroke: 'green' })
|
const startPoint = line.startPoint
|
||||||
canvas.add(line)
|
const endPoint = line.endPoint
|
||||||
canvas.renderAll()*/
|
|
||||||
const startPoint = { ...line.startPoint } // 시작점
|
|
||||||
let arrivalPoint = { ...line.endPoint } // 도착점
|
|
||||||
|
|
||||||
let currentPoint = startPoint
|
return innerLines.some((innerLine) => {
|
||||||
let roofPoints = [startPoint]
|
return (
|
||||||
|
isSamePoint(innerLine.startPoint, startPoint) ||
|
||||||
let startLine = line
|
isSamePoint(innerLine.endPoint, startPoint) ||
|
||||||
let visitPoints = [startPoint]
|
isSamePoint(innerLine.startPoint, endPoint) ||
|
||||||
let visitLines = [startLine]
|
isSamePoint(innerLine.endPoint, endPoint)
|
||||||
let notVisitedLines = []
|
|
||||||
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.length === 0) {
|
|
||||||
//아직 안갔던 line중 0번째를 선택한다.
|
|
||||||
if (notVisitedLines.length === 0) {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
let notVisitedLine = notVisitedLines.shift()
|
|
||||||
roofPoints = [...notVisitedLine.roofPoints]
|
|
||||||
currentPoint = { ...notVisitedLine.currentPoint }
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
} else {
|
|
||||||
notVisitedLines.push({
|
|
||||||
line: nextLine,
|
|
||||||
endPoint: nextLine.endPoint,
|
|
||||||
startPoint: nextLine.startPoint,
|
|
||||||
currentPoint: { ...currentPoint },
|
|
||||||
roofPoints: [...roofPoints],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
currentPoint = { ...minDistancePoint }
|
|
||||||
roofPoints.push(currentPoint)
|
|
||||||
cnt++
|
|
||||||
|
|
||||||
if (cnt > 100) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
roofs.push(roofPoints)
|
|
||||||
canvas.remove(line)
|
|
||||||
canvas.renderAll()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const newRoofs = removeDuplicatePolygons(roofs.filter((roof) => roof.length < 100))
|
// 나눠서 중복 제거된 roof return
|
||||||
|
const newRoofs = getSplitRoofsPoints(startLines, allLines, innerLines, uniquePoints)
|
||||||
|
|
||||||
newRoofs.forEach((roofPoint, index) => {
|
newRoofs.forEach((roofPoint, index) => {
|
||||||
let defense, pitch
|
let defense, pitch
|
||||||
@ -1104,7 +1077,7 @@ export const usePolygon = () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
// blue로 생성된 것들은 대표라인이 될 수 없음.
|
// blue로 생성된 것들은 대표라인이 될 수 없음.
|
||||||
representLines = representLines.filter((line) => line.stroke !== 'blue')
|
// representLines = representLines.filter((line) => line.stroke !== 'blue')
|
||||||
// representLines중 가장 긴 line을 찾는다.
|
// representLines중 가장 긴 line을 찾는다.
|
||||||
representLines.forEach((line) => {
|
representLines.forEach((line) => {
|
||||||
if (!representLine) {
|
if (!representLine) {
|
||||||
@ -1115,7 +1088,7 @@ export const usePolygon = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const direction = polygon.direction ?? representLine.direction
|
const direction = polygon.direction ?? representLine?.direction
|
||||||
const polygonDirection = polygon.direction
|
const polygonDirection = polygon.direction
|
||||||
|
|
||||||
switch (direction) {
|
switch (direction) {
|
||||||
@ -1174,6 +1147,113 @@ export const usePolygon = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getSplitRoofsPoints = (startLines, allLines, innerLines, uniquePoints) => {
|
||||||
|
// 거리 계산
|
||||||
|
function calcDistance(p1, p2) {
|
||||||
|
return Math.hypot(p2.x - p1.x, p2.y - p1.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 바로 연결 체크
|
||||||
|
function isDirectlyConnected(start, end, graph) {
|
||||||
|
const startKey = `${start.x},${start.y}`
|
||||||
|
if (!graph[startKey]) return false
|
||||||
|
return graph[startKey].some((neighbor) => neighbor.point.x === end.x && neighbor.point.y === end.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dijkstra 최단 경로
|
||||||
|
function findShortestPath(start, end, graph) {
|
||||||
|
const startKey = `${start.x},${start.y}`
|
||||||
|
const endKey = `${end.x},${end.y}`
|
||||||
|
|
||||||
|
const distances = {}
|
||||||
|
const previous = {}
|
||||||
|
const visited = new Set()
|
||||||
|
const queue = [{ key: startKey, dist: 0 }]
|
||||||
|
|
||||||
|
for (const key in graph) {
|
||||||
|
distances[key] = Infinity
|
||||||
|
}
|
||||||
|
distances[startKey] = 0
|
||||||
|
|
||||||
|
while (queue.length > 0) {
|
||||||
|
queue.sort((a, b) => a.dist - b.dist)
|
||||||
|
const { key } = queue.shift()
|
||||||
|
if (visited.has(key)) continue
|
||||||
|
visited.add(key)
|
||||||
|
|
||||||
|
for (const neighbor of graph[key]) {
|
||||||
|
const neighborKey = `${neighbor.point.x},${neighbor.point.y}`
|
||||||
|
const alt = distances[key] + neighbor.distance
|
||||||
|
if (alt < distances[neighborKey]) {
|
||||||
|
distances[neighborKey] = alt
|
||||||
|
previous[neighborKey] = key
|
||||||
|
queue.push({ key: neighborKey, dist: alt })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 경로 복구
|
||||||
|
const path = []
|
||||||
|
let currentKey = endKey
|
||||||
|
|
||||||
|
if (!previous[currentKey]) {
|
||||||
|
return null // 경로 없음
|
||||||
|
}
|
||||||
|
|
||||||
|
while (currentKey !== startKey) {
|
||||||
|
const [x, y] = currentKey.split(',').map(Number)
|
||||||
|
path.unshift({ x, y })
|
||||||
|
currentKey = previous[currentKey]
|
||||||
|
}
|
||||||
|
path.unshift(start)
|
||||||
|
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
// 최종 함수
|
||||||
|
function getPath(start, end, graph) {
|
||||||
|
if (isDirectlyConnected(start, end, graph)) {
|
||||||
|
return null
|
||||||
|
} else {
|
||||||
|
const path = findShortestPath(start, end, graph)
|
||||||
|
if (!path || path.length < 3) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const roofs = []
|
||||||
|
|
||||||
|
startLines.forEach((line) => {
|
||||||
|
// 그래프 생성
|
||||||
|
const graph = {}
|
||||||
|
const edges = allLines
|
||||||
|
.filter((line2) => line !== line2)
|
||||||
|
.map((line) => {
|
||||||
|
return [line.startPoint, line.endPoint]
|
||||||
|
})
|
||||||
|
|
||||||
|
for (const [p1, p2] of edges) {
|
||||||
|
const key1 = `${p1.x},${p1.y}`
|
||||||
|
const key2 = `${p2.x},${p2.y}`
|
||||||
|
const distance = calcDistance(p1, p2)
|
||||||
|
|
||||||
|
if (!graph[key1]) graph[key1] = []
|
||||||
|
if (!graph[key2]) graph[key2] = []
|
||||||
|
|
||||||
|
graph[key1].push({ point: p2, distance })
|
||||||
|
graph[key2].push({ point: p1, distance })
|
||||||
|
}
|
||||||
|
|
||||||
|
const startPoint = { ...line.startPoint } // 시작점
|
||||||
|
let arrivalPoint = { ...line.endPoint } // 도착점
|
||||||
|
roofs.push(getPath(startPoint, arrivalPoint, graph))
|
||||||
|
})
|
||||||
|
|
||||||
|
return removeDuplicatePolygons(roofs.filter((roof) => roof.length < 100))
|
||||||
|
}
|
||||||
|
|
||||||
const splitPolygonWithSeparate = (separates) => {
|
const splitPolygonWithSeparate = (separates) => {
|
||||||
separates.forEach((separate) => {
|
separates.forEach((separate) => {
|
||||||
const points = separate.lines.map((line) => {
|
const points = separate.lines.map((line) => {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user