[1514_3] ㅗ형 W-6 가로박공 마루 미생성 수정 — 중복 기반 SK 마루 제거 + 루프 폴백
Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
This commit is contained in:
parent
0d32f84ad7
commit
09ad09957b
@ -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 })
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user