From 45a3199fba9c121c31703bfc1170447507c2cb78 Mon Sep 17 00:00:00 2001 From: Jaeyoung Lee Date: Mon, 22 Jun 2026 13:07:20 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=EC=9A=A9=EB=A7=88=EB=A3=A8=20=EB=B9=84?= =?UTF-8?q?=EC=A7=81=EA=B5=90=20=ED=98=95=EC=83=81=20=EC=99=B8=EA=B3=BD=20?= =?UTF-8?q?=EC=A7=80=EB=B6=95=EC=84=A0=20=EC=86=8C=EC=8B=A4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [작업내용] : - splitRoofLinesToInnerLines 의 hasOverlapLine 가드를 축정렬 bbox 포함 → 공선+구간 덮음으로 교체 - 육각형 등 잘린 모서리의 짧은 수평/수직 처마변(bbox 한 축 두께 0)이 대각선 추녀 bbox 에 삼켜져 skip → 원본 외곽선 transparent 로 소실되던 false positive 제거 - 양 끝점이 모두 상대 선분 위(isPointOnLineNew)일 때만 overlap 인정 → 공선 아닌 추녀는 통과 - 변별/박공/한쪽흐름 경로 회귀 없음(직교 형상은 bbox 포함=공선 겹침이라 동작 불변) - 설계 문서 §10 진단 결과·수정·회귀 분석 기록 Co-Authored-By: Claude Opus 4.8 (1M context) --- ...026-06-01-skeleton-roofline-edit-design.md | 50 +++++++++++++++++++ src/util/qpolygon-utils.js | 26 +++++----- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/docs/plans/2026-06-01-skeleton-roofline-edit-design.md b/docs/plans/2026-06-01-skeleton-roofline-edit-design.md index 9e97a2d2..3164c6b2 100644 --- a/docs/plans/2026-06-01-skeleton-roofline-edit-design.md +++ b/docs/plans/2026-06-01-skeleton-roofline-edit-design.md @@ -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 유지. (캔버스 인터랙션이라 런타임 확인은 재현 필요 — 정적 분석상 ①만 동작 변화, ②③④ 불변 예상.) diff --git a/src/util/qpolygon-utils.js b/src/util/qpolygon-utils.js index 065d738a..19e27740 100644 --- a/src/util/qpolygon-utils.js +++ b/src/util/qpolygon-utils.js @@ -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