1111
This commit is contained in:
parent
89ae48d2fe
commit
de6d5b5211
@ -394,28 +394,8 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
|
|||||||
const isClosedPolygon = (points) =>
|
const isClosedPolygon = (points) =>
|
||||||
points.length > 1 && isSamePoint(points[0], points[points.length - 1])
|
points.length > 1 && isSamePoint(points[0], points[points.length - 1])
|
||||||
|
|
||||||
const roofLinePoints = isClosedPolygon(roof.points) ? roof.points.slice(0, -1) : roof.points
|
|
||||||
const orderedBaseLinePoints = isClosedPolygon(baseLinePoints) ? baseLinePoints.slice(0, -1) : baseLinePoints
|
const orderedBaseLinePoints = isClosedPolygon(baseLinePoints) ? baseLinePoints.slice(0, -1) : baseLinePoints
|
||||||
|
|
||||||
console.log('roofLinePoints:', roofLinePoints)
|
|
||||||
console.log('baseLinePoints:', orderedBaseLinePoints)
|
|
||||||
|
|
||||||
// baseLinePoint에서 roofLinePoint 방향으로 45도 대각선 확장 후 가장 가까운 접점 계산
|
|
||||||
// 각 포인트의 dx, dy, sign 정보 수집
|
|
||||||
const contactData = orderedBaseLinePoints.map((point, index) => {
|
|
||||||
const roofPoint = roofLinePoints[index]
|
|
||||||
if (!roofPoint) return { dx: 0, dy: 0, signDx: 0, signDy: 0 }
|
|
||||||
|
|
||||||
const dx = roofPoint.x - point.x
|
|
||||||
const dy = roofPoint.y - point.y
|
|
||||||
return { dx, dy, signDx: Math.sign(dx), signDy: Math.sign(dy) }
|
|
||||||
})
|
|
||||||
|
|
||||||
// maxStep: 모든 포인트 중 가장 큰 45도 step
|
|
||||||
const maxStep = contactData.reduce((max, data) => {
|
|
||||||
return Math.max(max, Math.min(Math.abs(data.dx), Math.abs(data.dy)))
|
|
||||||
}, 0)
|
|
||||||
|
|
||||||
// baseLine 폴리곤의 중심 계산 (확장 방향 결정용)
|
// baseLine 폴리곤의 중심 계산 (확장 방향 결정용)
|
||||||
const centroid = orderedBaseLinePoints.reduce((acc, p) => {
|
const centroid = orderedBaseLinePoints.reduce((acc, p) => {
|
||||||
acc.x += p.x / orderedBaseLinePoints.length
|
acc.x += p.x / orderedBaseLinePoints.length
|
||||||
@ -423,50 +403,74 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
|
|||||||
return acc
|
return acc
|
||||||
}, { x: 0, y: 0 })
|
}, { x: 0, y: 0 })
|
||||||
|
|
||||||
// 모든 포인트를 동일한 maxStep으로 45도 확장
|
// baseLine을 offset만큼 바깥으로 평행이동
|
||||||
let roofLineContactPoints = orderedBaseLinePoints.map((point, index) => {
|
const offsetLine = (line) => {
|
||||||
const data = contactData[index]
|
const sp = line.startPoint, ep = line.endPoint
|
||||||
const { dx, dy } = data
|
const offset = line.attributes?.offset ?? 0
|
||||||
|
const edx = ep.x - sp.x, edy = ep.y - sp.y
|
||||||
// dx 또는 dy가 0인 경우 중심에서 바깥쪽 방향으로 확장
|
const len = Math.hypot(edx, edy)
|
||||||
let signDx = data.signDx
|
if (len === 0) return { sp: { ...sp }, ep: { ...ep } }
|
||||||
let signDy = data.signDy
|
let nx = -edy / len, ny = edx / len
|
||||||
if (signDx === 0) {
|
const mid = { x: (sp.x + ep.x) / 2, y: (sp.y + ep.y) / 2 }
|
||||||
signDx = point.x >= centroid.x ? 1 : -1
|
if (nx * (centroid.x - mid.x) + ny * (centroid.y - mid.y) > 0) { nx = -nx; ny = -ny }
|
||||||
}
|
|
||||||
if (signDy === 0) {
|
|
||||||
signDy = point.y >= centroid.y ? 1 : -1
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: point.x + signDx * maxStep,
|
sp: { x: sp.x + nx * offset, y: sp.y + ny * offset },
|
||||||
y: point.y + signDy * maxStep
|
ep: { x: ep.x + nx * offset, y: ep.y + ny * offset }
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 두 직선의 교차점
|
||||||
|
const lineIntersect = (a1, a2, b1, b2) => {
|
||||||
|
const denom = (a1.x - a2.x) * (b1.y - b2.y) - (a1.y - a2.y) * (b1.x - b2.x)
|
||||||
|
if (Math.abs(denom) < 0.001) return null
|
||||||
|
const t = ((a1.x - b1.x) * (b1.y - b2.y) - (a1.y - b1.y) * (b1.x - b2.x)) / denom
|
||||||
|
return { x: a1.x + t * (a2.x - a1.x), y: a1.y + t * (a2.y - a1.y) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 각 꼭짓점에서 해당 점을 포함하는 두 baseLine을 찾아 offset 교차점으로 changRoofLinePoint 계산
|
||||||
|
let changRoofLinePoints = orderedBaseLinePoints.map((point) => {
|
||||||
|
const adjLines = baseLines.filter(line =>
|
||||||
|
isSamePoint(point, line.startPoint) || isSamePoint(point, line.endPoint)
|
||||||
|
)
|
||||||
|
if (adjLines.length < 2) return { ...point }
|
||||||
|
const oL1 = offsetLine(adjLines[0]), oL2 = offsetLine(adjLines[1])
|
||||||
|
return lineIntersect(oL1.sp, oL1.ep, oL2.sp, oL2.ep) || { ...point }
|
||||||
})
|
})
|
||||||
|
|
||||||
const maxContactDistance = Math.hypot(maxStep, maxStep)
|
// 중복 좌표 및 일직선 위의 불필요한 점 제거
|
||||||
|
const simplifyPoints = (pts) => {
|
||||||
let changRoofLinePoints = orderedBaseLinePoints.map((point, index) => {
|
let result = [...pts]
|
||||||
const contactPoint = roofLineContactPoints[index]
|
let changed = true
|
||||||
if (!contactPoint) return point
|
while (changed) {
|
||||||
|
changed = false
|
||||||
const dx = contactPoint.x - point.x
|
// 1) 중복 좌표 제거 (인접한 동일 좌표)
|
||||||
const dy = contactPoint.y - point.y
|
for (let i = result.length - 1; i >= 0; i--) {
|
||||||
const len = Math.hypot(dx, dy)
|
const next = result[(i + 1) % result.length]
|
||||||
|
if (Math.abs(result[i].x - next.x) < 0.5 && Math.abs(result[i].y - next.y) < 0.5) {
|
||||||
// 거리가 0이면 이미 같은 위치
|
result.splice(i, 1)
|
||||||
if (len === 0) return point
|
changed = true
|
||||||
|
|
||||||
const step = maxContactDistance
|
|
||||||
if (step === 0) return point
|
|
||||||
|
|
||||||
const nextX = point.x + (dx / len) * step
|
|
||||||
const nextY = point.y + (dy / len) * step
|
|
||||||
return {
|
|
||||||
x: nextX,
|
|
||||||
y: nextY
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
// 2) 일직선 위의 중간 점 제거 (cross product ≈ 0)
|
||||||
|
for (let i = result.length - 1; i >= 0 && result.length > 3; i--) {
|
||||||
|
const prev = result[(i - 1 + result.length) % result.length]
|
||||||
|
const curr = result[i]
|
||||||
|
const next = result[(i + 1) % result.length]
|
||||||
|
const cross = (curr.x - prev.x) * (next.y - prev.y) - (curr.y - prev.y) * (next.x - prev.x)
|
||||||
|
if (Math.abs(cross) < 1) {
|
||||||
|
result.splice(i, 1)
|
||||||
|
changed = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
changRoofLinePoints = simplifyPoints(changRoofLinePoints)
|
||||||
|
|
||||||
|
let roofLineContactPoints = [...changRoofLinePoints]
|
||||||
|
|
||||||
|
console.log('baseLinePoints:', orderedBaseLinePoints)
|
||||||
|
console.log('changRoofLinePoints:', changRoofLinePoints)
|
||||||
|
|
||||||
//마루이동
|
//마루이동
|
||||||
if (moveFlowLine !== 0 || moveUpDown !== 0) {
|
if (moveFlowLine !== 0 || moveUpDown !== 0) {
|
||||||
@ -478,8 +482,9 @@ export const skeletonBuilder = (roofId, canvas, textMode) => {
|
|||||||
// changRoofLinePoints 좌표를 roof.skeletonPoints에 저장 (원본 roof.points는 유지)
|
// changRoofLinePoints 좌표를 roof.skeletonPoints에 저장 (원본 roof.points는 유지)
|
||||||
roof.skeletonPoints = changRoofLinePoints.map(p => ({ x: p.x, y: p.y }))
|
roof.skeletonPoints = changRoofLinePoints.map(p => ({ x: p.x, y: p.y }))
|
||||||
|
|
||||||
console.log('points:', changRoofLinePoints)
|
console.log('skeleton polygon:', orderedBaseLinePoints)
|
||||||
const geoJSONPolygon = toGeoJSON(changRoofLinePoints)
|
console.log('expanded polygon:', changRoofLinePoints)
|
||||||
|
const geoJSONPolygon = toGeoJSON(orderedBaseLinePoints)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// SkeletonBuilder는 닫히지 않은 폴리곤을 기대하므로 마지막 점 제거
|
// SkeletonBuilder는 닫히지 않은 폴리곤을 기대하므로 마지막 점 제거
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user