dev #945
@ -7,6 +7,7 @@ import { LINE_TYPE, POLYGON_TYPE } from '@/common/common'
|
||||
import Big from 'big.js'
|
||||
import * as turf from '@turf/turf'
|
||||
|
||||
|
||||
const TWO_PI = Math.PI * 2
|
||||
const EPSILON = 1e-10 //좌표계산 시 최소 차이값
|
||||
|
||||
@ -4361,6 +4362,7 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
|
||||
right: nextIndex,
|
||||
type: TYPES.RIDGE,
|
||||
degree: 0,
|
||||
gableRidge: true, // [KERAB-GABLE-JOG-FIX 2026-07-02] 박공 파이프라인 생성 마루 식별
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -4425,6 +4427,44 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
|
||||
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 //무한루프 방지
|
||||
let iterations = 0
|
||||
@ -4439,6 +4479,14 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
|
||||
let linePoint = 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))
|
||||
//남은 길이가 0이면 무시
|
||||
if (cLength < EPSILON) return
|
||||
@ -4476,8 +4524,29 @@ export const drawRoofByAttribute = (roofId, canvas, textMode) => {
|
||||
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) {
|
||||
intersections.push({ index: i, intersect: intersectPoint, linePoint, partner })
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user