최단거리 roof 나누기 추가
This commit is contained in:
parent
46f46d734a
commit
5f648632dd
@ -1,7 +1,7 @@
|
||||
import { ANGLE_TYPE, canvasState, currentAngleTypeSelector, pitchTextSelector } from '@/store/canvasAtom'
|
||||
import { useRecoilValue } from 'recoil'
|
||||
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 { isSamePoint, removeDuplicatePolygons } from '@/util/qpolygon-utils'
|
||||
import { flowDisplaySelector } from '@/store/settingAtom'
|
||||
@ -757,7 +757,7 @@ export const usePolygon = () => {
|
||||
polygon.set({ visible: false })
|
||||
let innerLines = [...polygon.innerLines]
|
||||
|
||||
// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다.
|
||||
/*// innerLine이 세팅이 안되어있는경우 찾아서 세팅한다.
|
||||
if (!innerLines || innerLines.length === 0) {
|
||||
let innerLineTypes = Object.keys(LINE_TYPE.SUBLINE).map((key, value) => LINE_TYPE.SUBLINE[key])
|
||||
polygon.innerLines = canvas
|
||||
@ -771,8 +771,7 @@ export const usePolygon = () => {
|
||||
)
|
||||
|
||||
innerLines = [...polygon.innerLines]
|
||||
}
|
||||
|
||||
}*/
|
||||
canvas.renderAll()
|
||||
let polygonLines = [...polygon.lines]
|
||||
const roofs = []
|
||||
@ -830,8 +829,14 @@ export const usePolygon = () => {
|
||||
canvas.renderAll()
|
||||
|
||||
polygonLines.forEach((line) => {
|
||||
/*const originStroke = line.stroke
|
||||
line.set({ stroke: 'red' })
|
||||
canvas.renderAll()*/
|
||||
const intersections = []
|
||||
innerLines.forEach((innerLine) => {
|
||||
/*const originInnerStroke = innerLine.stroke
|
||||
innerLine.set({ stroke: 'red' })
|
||||
canvas.renderAll()*/
|
||||
if (isPointOnLine(line, innerLine.startPoint)) {
|
||||
if (isSamePoint(line.startPoint, innerLine.startPoint) || isSamePoint(line.endPoint, innerLine.startPoint)) {
|
||||
return
|
||||
@ -844,8 +849,13 @@ export const usePolygon = () => {
|
||||
}
|
||||
intersections.push(innerLine.endPoint)
|
||||
}
|
||||
/*innerLine.set({ stroke: originInnerStroke })
|
||||
canvas.renderAll()*/
|
||||
})
|
||||
line.set({ intersections })
|
||||
|
||||
/*line.set({ stroke: originStroke })
|
||||
canvas.renderAll()*/
|
||||
})
|
||||
|
||||
const divideLines = polygonLines.filter((line) => line.intersections.length > 0)
|
||||
@ -924,10 +934,52 @@ export const usePolygon = () => {
|
||||
})
|
||||
|
||||
//polygonLines에서 divideLines를 제거하고 newLines를 추가한다.
|
||||
polygonLines = polygonLines.filter((line) => !divideLines.includes(line))
|
||||
polygonLines = polygonLines.filter((line) => line.intersections?.length === 0)
|
||||
|
||||
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 대각선은 케라바, 직선은 용마루로 세팅
|
||||
innerLines.forEach((innerLine) => {
|
||||
@ -978,102 +1030,23 @@ export const usePolygon = () => {
|
||||
line.endPoint = endPoint
|
||||
})
|
||||
|
||||
// polygon line에서 각각 출발한다.
|
||||
polygonLines.forEach((line) => {
|
||||
/*line.set({ strokeWidth: 5, stroke: 'green' })
|
||||
canvas.add(line)
|
||||
canvas.renderAll()*/
|
||||
const startPoint = { ...line.startPoint } // 시작점
|
||||
let arrivalPoint = { ...line.endPoint } // 도착점
|
||||
// polygonLines에서 시작점 혹은 끝점이 innerLines와 연결된 line만 가져온다.
|
||||
let startLines = polygonLines.filter((line) => {
|
||||
const startPoint = line.startPoint
|
||||
const endPoint = line.endPoint
|
||||
|
||||
let currentPoint = startPoint
|
||||
let roofPoints = [startPoint]
|
||||
|
||||
let startLine = line
|
||||
let visitPoints = [startPoint]
|
||||
let visitLines = [startLine]
|
||||
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),
|
||||
return innerLines.some((innerLine) => {
|
||||
return (
|
||||
isSamePoint(innerLine.startPoint, startPoint) ||
|
||||
isSamePoint(innerLine.endPoint, startPoint) ||
|
||||
isSamePoint(innerLine.startPoint, endPoint) ||
|
||||
isSamePoint(innerLine.endPoint, endPoint)
|
||||
)
|
||||
|
||||
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) => {
|
||||
let defense, pitch
|
||||
@ -1104,7 +1077,7 @@ export const usePolygon = () => {
|
||||
})
|
||||
|
||||
// blue로 생성된 것들은 대표라인이 될 수 없음.
|
||||
representLines = representLines.filter((line) => line.stroke !== 'blue')
|
||||
// representLines = representLines.filter((line) => line.stroke !== 'blue')
|
||||
// representLines중 가장 긴 line을 찾는다.
|
||||
representLines.forEach((line) => {
|
||||
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
|
||||
|
||||
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) => {
|
||||
separates.forEach((separate) => {
|
||||
const points = separate.lines.map((line) => {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user