최단거리 roof 나누기 추가

This commit is contained in:
hyojun.choi 2025-04-28 18:07:54 +09:00
parent 46f46d734a
commit 5f648632dd

View File

@ -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) => {