5.3 KiB
| name | description | type |
|---|---|---|
| 평행 이동 SK 붕괴 근본 원인 및 수정 (2차) | 평행 이동 시 roof.basePoints[index] 좌표 매칭이 wall 오프셋 붕괴(degenerate baseLines)·좌표 우연 일치에 취약해 엉뚱한 꼭짓점에 dy 적용 → SK 축직교 깨짐 → SkeletonBuilder 보조선 난립/붕괴. movingLineFromSkeleton 평행 분기와 buildRawMovedPoints를 인덱스 직접 매핑 + Set 합집합으로 전환. | project |
사실: skeleton-utils.js의 평행 이동 dy 적용 경로 2곳을 좌표 매칭 → 인덱스 직접 매핑으로 전환 (2026-04-23 오후).
movingLineFromSkeleton평행 분기:matchingLines.forEach + newPoints.forEach((point, index) => isSamePoint(roof.basePoints[index], originalStart/End))제거.line.findIndex = k기반affected = {k, (k+1)%n}Set 합집합 →affected.forEach(i => apply dy)로 교체.buildRawMovedPoints: 동일 패턴으로 교체.matchingLines는 LP-TRACE 로그용으로{line, idx}쌍 형태 유지.
Why: createOrderedBasePoints가 오전 커밋(0d3ede96 보조라인 맞춤)에서 baseLines[i].startPoint 직접 인덱스 매핑으로 고쳐졌지만, wall 오프셋 계산 단계에서 이미 baseLines[k]가 degenerate(zero-length 또는 인접 edge와 start 좌표 공유) 가 된 형상(L자 shoulder, ㅗ의 shoulder 등)에서는 roof.basePoints[k] 가 다른 edge 꼭짓점과 좌표 일치하게 되어 좌표 매칭이 오염됨. polygon topology 로 정해지는 인덱스 k 는 이런 좌표 붕괴에 독립적.
Why (Set 합집합): matchingLines ≥ 2 케이스(보강 라인 중복 push, coincident edges)에서 같은 꼭짓점에 dy 가 중복 적용되는 잠재 버그도 동시에 제거.
How to apply:
moveFlowLine(단일 라인 이동) 경로와 무관. 오전에 안정화된 단일 라인 이동 로직은 건드리지 않음.createOrderedBasePoints/roof.basePoints는 다른 경로가 참조할 수 있어 유지. 이 두 지점만 우회.- 평행 누적/붕괴 이동 증상(예: 첫 top_out 에서
verifyMoveBoundary moved=[0, k-1, k]처럼 의도하지 않은 인접 꼭짓점이 함께 이동) 재현 시 이 패치가 적용되어 있는지 먼저 확인.
관련 커밋 히스토리:
4d9e2fc0 top 평행수정(오전):verifyMoveBoundary로깅만 추가.0d3ede96 보조라인 맞춤(오전):createOrderedBasePointsgraph traversal → 인덱스 직접 매핑. 단, dy 적용 경로는 여전히 좌표 매칭이라 이 패치(2차)로 보완.
죽은 코드 주의: skeleton-utils.js의 correctedPoints 교정 블록(skeletonBuilder 내)은 0d3ede96에서 블록 주석 처리된 죽은 코드. 건드리지 않는다.
3차 후속 수정 (2026-04-23 저녁): SHOULDER_ABSORBED guard
사실: skeleton-utils.js:1482 pair forEach 엔트리에 zero-length wallBaseLine 스킵 가드 추가 (planeSize < 1 체크).
Why: useMovementSetting.js:handleSave 의 이웃 흡수 로직 (line 766-782) 이 shoulder 가 이동량에 의해 흡수되는 경우 nextLine/prevLine 을 (x,y)→(x,y) zero-length 로 mutate 하고 planeSize=0 으로 저장. 이는 의도된 geometry 표현 (흡수된 edge 는 "없어진" 상태). 다만 downstream skeleton-utils.js pair 매칭에서 이를 그대로 받아 ⚠️DIR_MISMATCH 로그 + 잘못된 eaveHelpLine 생성 → SK 보조선 난립.
How to apply:
skeleton-utils.jspair 생성부sortWallLines.forEach((wallLine, index) => { ... })최상단에if ((wallBaseLine.attributes?.planeSize ?? Infinity) < 1) return가드.- 같은 파일 line 666 의
wall.baseLines.filter(line => line.attributes.planeSize > 0)선례와 동일 패턴. - 생산지(
useMovementSetting.js) 는 의도대로 동작 중이라 건드리지 않음 (잘 되는 코드 건들지 않기 원칙). - 대안이었던 "sortWallBaseLines 배열 자체 필터" 는
sortRoofLines[index]/sortWallLines[index]와 인덱스 align 이 깨져 불가. per-iteration guard 가 최소 변경.
재현 체크: ⏭️ [pair#N] SHOULDER_ABSORBED skip 로그가 찍히면 가드가 걸린 것.
4차 후속 수정 (2026-04-23 저녁): HIP_SKIP adj_absorbed guard
사실: skeleton-utils.js:processInBoth 진입부에 인접 pair 흡수 감지 시 HIP extensionLine 생성 스킵 가드 추가.
Why: SHOULDER_ABSORBED 가드로 pair forEach 의 DIR_MISMATCH 는 해결됐지만, 남은 인접 pair 의 processInBoth 가 원본 roof.points 기반 꼭짓점 (흡수로 사라진 shoulder 위치) 으로 HIP 빗변 끝점을 설정해 "공중에 뜬 orange 대각선" 을 만들어냄. 화면상 blue 사각형(simplified ridge) 외곽 코너에 길이 70.7 짜리 빗변이 orphan 으로 남는 증상.
How to apply:
processInBoth함수 최상단에sortWallBaseLines[(index±1)%n].attributes.planeSize < 1체크, 인접 중 하나라도 흡수된 경우 early return.- processInBoth 는 getMoveUpDownLine 스코프 내부 정의라 sortWallBaseLines 클로저 접근 가능.
- 호출처 2곳 (line 1675, 1930) 에 중복 가드 안 넣고 함수 진입부 한 곳에서 처리.
- processInStartEnd (blue/green/pink/red 보조선) 은 건드리지 않음 — HIP 빗변 전용 문제.
재현 체크: ⏭️ [pair#N] X_in HIP_SKIP adj_absorbed prev=T/F next=T/F 로그로 확인.