dev #931
@ -197,3 +197,53 @@ opts.degreeOf?: (parentLine) => number // 부모 외곽변 → 경사각(도).
|
||||
- offset=0 / offset≠0 양쪽 동작.
|
||||
- 마루이동/벽이동 후 재그리기에도 유지(연장은 `__extended`/`attributes.extended` 로 영속).
|
||||
- **임시 계측(`logSkRoofLineAlignment` 정의 + 호출부 3곳 `:1279`/`:1285`/`:1413`) 제거** 후 커밋.
|
||||
|
||||
---
|
||||
|
||||
## 10. 육각형 등 비직교 형상에서 외곽 지붕선 소실 — 진단 (2026-06-22 추가)
|
||||
|
||||
### 10-1. 증상
|
||||
**육각형(wall 6변) 용마루 지붕** 설정 시 오른쪽 위 외곽 지붕선 2개가 화면에서 사라진다.
|
||||
단순 우진각(사각형)은 정상(§9-1 실측 검증 완료) — 비직교(대각선) 변이 많은 형상에서만 발생.
|
||||
|
||||
### 10-2. 가설 (코드 분석, 1순위)
|
||||
`splitRoofLinesToInnerLines`(`qpolygon-utils.js:5485`) 는 처마변을 innerLine 으로 등록한 뒤
|
||||
**roof 원본 외곽선을 `stroke:'transparent'` 로 숨긴다**(`:5563`). 따라서 어떤 처마변이
|
||||
innerLine 으로 **재등록되지 못하면** 원본 대체선도 없어 그 자리에 선이 완전히 사라진다.
|
||||
|
||||
누락 1순위 지점 = `hasOverlapLine` 가드(`:5511-5523`). 이는 **축정렬 bounding-box 포함관계**로만
|
||||
overlap 을 판정하는데, 이 블록은 원래 **변별 경로(`drawRoofByAttribute`, 주로 직교 형상)** 의
|
||||
인라인 코드를 `1bf5f45b` 에서 공용 추출한 것이다. 직교 변은 bbox 가 선분에 딱 맞아 false positive 가
|
||||
드물지만, **육각형의 대각선 처마변 + 대각선 추녀(hip)** 는 bbox 가 큰 사각형이라 코너를 공유할 때
|
||||
서로의 bbox 를 포함하기 쉬워 `hasOverlapLine=true` 오발동 → 해당 처마변 `return`(skip).
|
||||
오른쪽 위 짧은 대각선 변 2개가 인접 추녀 bbox 에 함께 포함되어 동시 skip 된 것으로 추정.
|
||||
|
||||
### 10-3. 진단 결과 — 가설 확정 (2026-06-22)
|
||||
육각형(직사각형 우상단 모서리를 계단으로 자른 형상) 재현 `[HEXDIAG]` 로그:
|
||||
|
||||
split entry roof.lines=6 innerLines=9
|
||||
line#0 WHOLE (386,35)→(386,1335) ← 좌 수직변, 정상
|
||||
line#1 WHOLE (386,1335)→(1286,1335) ← 하 수평변, 정상
|
||||
line#2 WHOLE (1286,1335)→(1286,235) ← 우 수직변, 정상
|
||||
line#3 SKIP (1286,235)→(1086,235) overlapBy=inner⊇curr hip(1286,235)→(836,685) ✗
|
||||
line#4 SKIP (1086,235)→(1086,35) overlapBy=inner⊇curr hip(1086,35)→(736,385) ✗
|
||||
line#5 WHOLE (1086,35)→(386,35) ← 상 수평변, 정상
|
||||
|
||||
- 사라진 line#3(수평, y두께 0)·line#4(수직, x두께 0) = 잘린 계단의 짧은 변 2개.
|
||||
- 둘 다 `inner⊇curr`: 끝점 코너를 공유한 **대각선 추녀(hip)의 bbox 가 0-두께 처마변 bbox 를 포함**.
|
||||
예) line#3 bbox `x[1086,1286]·y[235,235]` ⊂ hip bbox `x[836,1286]·y[235,685]`.
|
||||
- line#0·1·2·5 가 멀쩡한 건 길어서 어떤 hip bbox 로도 통째로 못 감싸기 때문. → **가설 확정.**
|
||||
- 1순위 정정: 범인은 "대각선 처마변"이 아니라 **축정렬(수평/수직) 짧은 처마변 + 대각선 추녀** 조합.
|
||||
|
||||
### 10-4. 수정 (적용 완료, 2026-06-22)
|
||||
`hasOverlapLine` 판정을 **bbox 포함 → 공선 + 구간 덮음**으로 교체(`qpolygon-utils.js:5507-5519`):
|
||||
한쪽 선분의 **양 끝점이 모두 상대 선분 위**(`isPointOnLineNew`, tol 0.1)일 때만 overlap 인정.
|
||||
대각선 추녀는 처마변과 공선이 아니라 통과 → 처마변 정상 등록. `[HEXDIAG]` 계측·`logger` import 전량 제거.
|
||||
|
||||
**회귀 영향 분석** (이 함수를 쓰는 4경로):
|
||||
- **변별**(`drawRoofByAttribute` → `:5447`): 가드 원사용처. 원래 bbox 가 잡던 "진짜 같은 선 위 겹침"은 공선이라 새 판정으로도 잡힘. bbox-만-겹침(공선 아님)은 원래 false positive → 제거가 곧 개선. 회귀 위험 = 공선인데 부동소수로 tol 0.1 초과하는 경계뿐(드묾).
|
||||
- **한쪽흐름**(`drawShedRoof` → `:1731`): 내부 보조선 없어 `innerLines` 거의 비어 가드 트리거 자체가 드묾. 영향 미미.
|
||||
- **용마루**(skeleton `:1288`): 대상 케이스. 사각형 우진각·육각형 양쪽 확인.
|
||||
- **박공**(`drawGableRoof`): split 미사용 → 무영향.
|
||||
|
||||
**재현 검증 체크리스트**: ① 육각형 용마루 6변 전부 등록(소실 없음) ② 단순 사각형 우진각 4변 유지(회귀 없음) ③ 변별 지붕 외곽선·추녀 정상 ④ 한쪽흐름 경사변 actual=plane/cos 유지. (캔버스 인터랙션이라 런타임 확인은 재현 필요 — 정적 분석상 ①만 동작 변화, ②③④ 불변 예상.)
|
||||
|
||||
@ -5504,21 +5504,19 @@ export const splitRoofLinesToInnerLines = (roof, innerLines, canvas, textMode, o
|
||||
|
||||
const splitPoint = []
|
||||
let hasOverlapLine = false
|
||||
const minX = Math.min(currentLine.x1, currentLine.x2)
|
||||
const maxX = Math.max(currentLine.x1, currentLine.x2)
|
||||
const minY = Math.min(currentLine.y1, currentLine.y2)
|
||||
const maxY = Math.max(currentLine.y1, currentLine.y2)
|
||||
innerLines.forEach((innerLine) => {
|
||||
const innerLineMinX = Math.min(innerLine.x1, innerLine.x2)
|
||||
const innerLineMaxX = Math.max(innerLine.x1, innerLine.x2)
|
||||
const innerLineMinY = Math.min(innerLine.y1, innerLine.y2)
|
||||
const innerLineMaxY = Math.max(innerLine.y1, innerLine.y2)
|
||||
if (innerLineMinX <= minX && innerLineMaxX >= maxX && innerLineMinY <= minY && innerLineMaxY >= maxY) {
|
||||
hasOverlapLine = true
|
||||
}
|
||||
if (minX <= innerLineMinX && maxX >= innerLineMaxX && minY <= innerLineMinY && maxY >= innerLineMaxY) {
|
||||
hasOverlapLine = true
|
||||
}
|
||||
// [docs §10] overlap 판정: 축정렬 bbox 포함 → "공선 + 구간 덮음" 으로 변경.
|
||||
// 기존 bbox 포함은 수평/수직 짧은 처마변(bbox 한 축 두께 0)을 대각선 추녀(hip) bbox 가
|
||||
// 삼켜 false positive(처마변 skip → 원본 transparent → 지붕선 소실)를 냈다(육각형 잘린 모서리).
|
||||
// 원 가드 의도("같은 선 위 더 긴 내부선이 처마변을 덮으면 중복이니 skip")는 공선이 전제이므로,
|
||||
// 공선 아닌 추녀는 애초에 skip 대상이 아니다. 양 끝점이 상대 선분 위(isPointOnLineNew)면 덮음.
|
||||
const innerCoversCurr =
|
||||
isPointOnLineNew(innerLine, { x: currentLine.x1, y: currentLine.y1 }) &&
|
||||
isPointOnLineNew(innerLine, { x: currentLine.x2, y: currentLine.y2 })
|
||||
const currCoversInner =
|
||||
isPointOnLineNew(currentLine, { x: innerLine.x1, y: innerLine.y1 }) &&
|
||||
isPointOnLineNew(currentLine, { x: innerLine.x2, y: innerLine.y2 })
|
||||
if (innerCoversCurr || currCoversInner) hasOverlapLine = true
|
||||
})
|
||||
if (hasOverlapLine) return
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user