[1514_3] ㅗ형 W-6 가로박공 마루 미생성 수정 — 중복 기반 SK 마루 제거 + 루프 폴백

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
ysCha 2026-07-02 11:32:09 +09:00
parent 0d32f84ad7
commit 09ad09957b

View File

@ -7,6 +7,7 @@ import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
import Big from 'big.js' import Big from 'big.js'
import * as turf from '@turf/turf' import * as turf from '@turf/turf'
const TWO_PI = Math.PI * 2 const TWO_PI = Math.PI * 2
const EPSILON = 1e-10 //좌표계산 시 최소 차이값 const EPSILON = 1e-10 //좌표계산 시 최소 차이값
@ -4361,6 +4362,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
right: nextIndex, right: nextIndex,
type: TYPES.RIDGE, type: TYPES.RIDGE,
degree: 0, degree: 0,
gableRidge: true, // [KERAB-GABLE-JOG-FIX 2026-07-02] 박공 파이프라인 생성 마루 식별
}) })
} }
} }
@ -4425,6 +4427,44 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
canvas.renderAll() canvas.renderAll()
}) })
// [KERAB-GABLE-JOG-FIX 2026-07-02] 박공 마루(gableRidge)가 생기면 그 팔 안의 중심선 마루 하나만 유효하다.
// 기반 스켈레톤(proceedRidges)이 뱉는, 박공 마루와 평행·index공유·같은 팔에 겹치는 중복 세로/가로 마루는 제거한다.
// (처마일 때는 apex 로 흡수되던 마루가, 박공 마루 추가로 index 를 공유하며 이중마루로 남는 것을 막는다.)
{
const gableRidges = linesAnalysis.filter((l) => l.type === TYPES.RIDGE && l.gableRidge)
if (gableRidges.length > 0) {
const isVert = (l) => Math.abs(l.start.x - l.end.x) < Math.abs(l.start.y - l.end.y)
const removeSet = new Set()
linesAnalysis.forEach((base) => {
if (base.type !== TYPES.RIDGE || base.gableRidge) return
gableRidges.forEach((g) => {
if (isVert(base) !== isVert(g)) return // 평행만
const shareIdx = base.left === g.left || base.left === g.right || base.right === g.left || base.right === g.right
if (!shareIdx) return
const gLen = Math.hypot(g.end.x - g.start.x, g.end.y - g.start.y)
if (isVert(g)) {
const bMin = Math.min(base.start.y, base.end.y)
const bMax = Math.max(base.start.y, base.end.y)
const gMin = Math.min(g.start.y, g.end.y)
const gMax = Math.max(g.start.y, g.end.y)
const overlap = Math.min(bMax, gMax) - Math.max(bMin, gMin) > EPSILON
if (overlap && Math.abs(base.start.x - g.start.x) < gLen) removeSet.add(base)
} else {
const bMin = Math.min(base.start.x, base.end.x)
const bMax = Math.max(base.start.x, base.end.x)
const gMin = Math.min(g.start.x, g.end.x)
const gMax = Math.max(g.start.x, g.end.x)
const overlap = Math.min(bMax, gMax) - Math.max(bMin, gMin) > EPSILON
if (overlap && Math.abs(base.start.y - g.start.y) < gLen) removeSet.add(base)
}
})
})
if (removeSet.size > 0) {
linesAnalysis = linesAnalysis.filter((l) => !removeSet.has(l))
}
}
}
//각 선분의 교차점을 찾아 선을 그린다. //각 선분의 교차점을 찾아 선을 그린다.
const MAX_ITERATIONS = 1000 //무한루프 방지 const MAX_ITERATIONS = 1000 //무한루프 방지
let iterations = 0 let iterations = 0
@ -4439,6 +4479,14 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
let linePoint = null let linePoint = null
let partner = null let partner = null
// [KERAB-GABLE-JOG-FIX 2026-07-02] 인덱스를 공유하는(=정당한 접합 가능) 최근접 파트너를 별도 추적.
// ㅗ 노치의 계단형 마루(offset 평행)처럼 인덱스 미공유 교점이 최근접으로 끼어들어 상호최단을 가로채면,
// 마루↔힙의 정당한 접합이 막혀 마루가 소멸한다. 전역 최근접이 인덱스 미공유일 때만 이 후보로 폴백한다.
let sameMinDistance = Infinity
let sameIntersectPoint = null
let sameLinePoint = null
let samePartner = null
const cLength = Math.sqrt(Math.pow(currLine.end.x - currLine.start.x, 2) + Math.pow(currLine.end.y - currLine.start.y, 2)) const cLength = Math.sqrt(Math.pow(currLine.end.x - currLine.start.x, 2) + Math.pow(currLine.end.y - currLine.start.y, 2))
//남은 길이가 0이면 무시 //남은 길이가 0이면 무시
if (cLength < EPSILON) return if (cLength < EPSILON) return
@ -4476,8 +4524,29 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
partner = j partner = j
} }
} }
// [KERAB-GABLE-JOG-FIX 2026-07-02] 인덱스 공유(정당한 접합) 최근접 후보를 별도로 추적
const shareIdx =
currLine.left === nextLine.left || currLine.left === nextLine.right || currLine.right === nextLine.left || currLine.right === nextLine.right
if (shareIdx && distance1 < sameMinDistance && !almostEqual(distance1, sameMinDistance) && !(distance1 < EPSILON && distance2 < EPSILON)) {
sameMinDistance = distance1
sameIntersectPoint = intersect
sameLinePoint = point
samePartner = j
}
} }
}) })
// [KERAB-GABLE-JOG-FIX 2026-07-02] 전역 최근접 파트너가 인덱스 미공유(접합 불가)이면, 인덱스 공유 최근접으로 폴백
if (partner !== null) {
const pLine = linesAnalysis[partner]
const chosenSame =
currLine.left === pLine.left || currLine.left === pLine.right || currLine.right === pLine.left || currLine.right === pLine.right
if (!chosenSame && samePartner !== null) {
minDistance = sameMinDistance
intersectPoint = sameIntersectPoint
linePoint = sameLinePoint
partner = samePartner
}
}
if (intersectPoint) { if (intersectPoint) {
intersections.push({ index: i, intersect: intersectPoint, linePoint, partner }) intersections.push({ index: i, intersect: intersectPoint, linePoint, partner })
} }