[지붕면] 지붕면 할당 inner-inner T-접합 분할 보완 — A/B TYPE 박공→처마 우측 면 미생성 수정
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
parent
d0c3110801
commit
9c6cabe911
@ -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
|
||||
let newRoofs = getSplitRoofsPoints(allLines)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user