[지붕면] 지붕면 할당 inner-inner T-접합 분할 보완 — A/B TYPE 박공→처마 우측 면 미생성 수정

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
ysCha 2026-06-29 13:09:06 +09:00
parent d0c3110801
commit 9c6cabe911

View File

@ -1664,6 +1664,81 @@ export const usePolygon = () => {
} }
} }
// [INNER-TJUNCTION-SPLIT 2026-06-29] 내부선(추녀 hip / 용마루 ridge)의 끝점이 다른 내부선의 몸통(strict
// interior)에 닿는 T-접합인데 그 내부선에 노드가 없으면, getPath 가 그 지점에서 꺾지 못해 한쪽 면이
// 통째로 누락된다. (A/B TYPE 박공→처마: ridge x=747(64.2→266.4) 의 내부 (747,236.4) 에 hip
// (662.3,236.4)→(747,236.4) 끝점이 닿는데 ridge 가 안 잘려 우측 6각형 면이 안 생김 — GETSPLIT-IO 진단.)
// MOVED-DIVIDER 는 auxiliaryLine 끝점만, 외곽 split 루프는 외곽선만 자른다 → inner-inner T-접합은 둘 다 미커버.
// 여기서 다른 내부선 끝점 위치에서 내부선을 split(노드 삽입)만 한다. merge 없음 — 정상 평면분할 노드를 추가만 하므로
// 기존에 닫히던 면은 불변(노드가 명시될 뿐).
{
const isInnerT = (l) => l.name === 'hip' || l.name === 'ridge'
const EPS_T = 2
const mkSegT = (proto, sp, ep) => ({
name: proto.name,
lineName: proto.lineName,
attributes: { ...proto.attributes },
startPoint: { x: sp.x, y: sp.y },
endPoint: { x: ep.x, y: ep.y },
x1: sp.x,
y1: sp.y,
x2: ep.x,
y2: ep.y,
})
const onBodyStrictT = (L, P) => {
const ax = L.startPoint.x
const ay = L.startPoint.y
const dx = L.endPoint.x - ax
const dy = L.endPoint.y - ay
const lenSq = dx * dx + dy * dy
if (lenSq < 1) return false
const t = ((P.x - ax) * dx + (P.y - ay) * dy) / lenSq
if (t <= 0.03 || t >= 0.97) return false
const px = ax + t * dx
const py = ay + t * dy
return (P.x - px) ** 2 + (P.y - py) ** 2 < EPS_T * EPS_T
}
const innerLinesT = allLines.filter(isInnerT)
if (innerLinesT.length > 1) {
const innerPts = []
innerLinesT.forEach((l) => {
innerPts.push(l.startPoint, l.endPoint)
})
let didTSplit = false
const afterT = []
allLines.forEach((L) => {
if (!isInnerT(L)) {
afterT.push(L)
return
}
const ax = L.startPoint.x
const ay = L.startPoint.y
const dx = L.endPoint.x - ax
const dy = L.endPoint.y - ay
const lenSq = dx * dx + dy * dy || 1
const tOf = (P) => ((P.x - ax) * dx + (P.y - ay) * dy) / lenSq
const cuts = [...new Map(innerPts.filter((P) => onBodyStrictT(L, P)).map((P) => [Math.round(tOf(P) * 1000), P])).values()].sort(
(p, q) => tOf(p) - tOf(q),
)
if (cuts.length === 0) {
afterT.push(L)
return
}
didTSplit = true
let cur = L.startPoint
cuts.forEach((P) => {
afterT.push(mkSegT(L, cur, P))
cur = P
})
afterT.push(mkSegT(L, cur, L.endPoint))
})
if (didTSplit) {
logger.log(`[INNER-TJUNCTION-SPLIT] inner-inner T-접합 노드 삽입 (allLines ${allLines.length}${afterT.length})`)
allLines = afterT
}
}
}
// 나눠서 중복 제거된 roof return // 나눠서 중복 제거된 roof return
let newRoofs = getSplitRoofsPoints(allLines) let newRoofs = getSplitRoofsPoints(allLines)