7.0 KiB
7.0 KiB
| name | description | type |
|---|---|---|
| 오버 이동은 평행 위치까지 클램핑 (mutation 거부 아님) | useMovementSetting.handleSave 의 mutation 직전에 OVER_GUARD 를 두어 인접 라인을 0 길이로 흡수하는 평행 위치까지 deltaX/deltaY 를 잘라 적용한다. 사용자가 입력한 큰 값도 평행 한계까지는 그대로 반영되고, 그 이상은 흡수 위치에서 멈춘다. UI 알림은 띄우지 않고 콘솔 경고만. | feedback |
현행 정책 (2026-04-24, 최종): 사용자가 입력한 이동량이 인접 라인을 뒤집어 폴리곤 self-cross 를 일으키는 수준이면, mutation 을 거부하지 않고 평행 위치(인접 라인이 정확히 0 길이가 되는 한계)까지로 클램핑 한다. wall.baseLines 는 절대 오버 좌표로 갱신되지 않으므로 SK 빌드도 항상 정상 가정 하에 동작.
정책 전환 flag (2026-04-24 추가):
- 위치:
src/hooks/roofcover/useMovementSetting.js파일 상단const OVER_MOVE_POLICY = 'clamp'. - 값:
'clamp'(기본) /'reject'(silent skip) /'passthrough'(가드 전면 해제). - 교체 방법: 이 상수 하나만 바꾸면 handleSave 내 OVER_GUARD 분기가 자동 반응.
- Why: 사용자가 정책을 과거로 되돌리거나 임시 실험하고 싶을 때 분기 코드 수정 없이 전환.
OVER_GUARD 위치 & 판정:
- 파일:
src/hooks/roofcover/useMovementSetting.js, handleSave 의targetBaseLines.forEach((target) => { ... })안. - 위치: deltaX/deltaY 결정 직후,
currentLine.set({...})직전. - 클램핑 식 (currentLine 이 수평이면 deltaY, 수직이면 deltaX 가 유일 성분):
raw = isHoriz ? deltaY : deltaX sgn = Math.sign(raw) nLimit = isHoriz ? (nextLine.y2 - currentLine.y2) : (nextLine.x2 - currentLine.x2) pLimit = isHoriz ? (prevLine.y1 - currentLine.y1) : (prevLine.x1 - currentLine.x1) // sgn 과 같은 부호 한계만 후보 (반대 부호는 인접 라인이 길어지는 방향이므로 오버 아님) absLimit = min of |nLimit|, |pLimit| (sgn 일치하는 것만) // [EPS 필수] 정확한 평행은 인접 baseLine zero-length → polygon 중복 꼭짓점 → SK 빌더가 헛선 추가. // OVER_EPS 만큼 빼서 인접 baseLine 이 0.5 길이로 남게 → 기존 SHOULDER_ABSORBED 가드가 자연 처리. safeAbsLimit = max(0, absLimit - OVER_EPS) // OVER_EPS = 0.5 limited = sgn * min(|raw|, safeAbsLimit) |limited| < |raw|이면 클램핑 발생 → 콘솔에[handleSave] OVER 클램핑: deltaX|deltaY raw→limited (nLimit=... pLimit=..., EPS=0.5).- 토스트/모달 알림 없음.
[필수] roof.moveUpDown 동시 클램핑:
- 클램핑 블록 안에서
roof.moveUpDown = Math.round(Math.abs(__limited) * 10)을 같이 해줘야 한다. - 이유:
movingLineFromSkeleton(skeleton-utils.js) 은 wall.baseLines 가 아니라roof.moveUpDown+canvas.skeleton.lastPoints(또는orgRoofPoints) 로부터 roof.points 확장량을 계산한다. wall.baseLines delta 만 줄이고roof.moveUpDown을 방치하면:- wall.baseLines 는 평행 위치까지만 이동 (클램핑 됨)
- roof.points 는 원본 입력값(예: 1525) 기반 확장 적용
- 두 좌표계 충돌 → polygon 자기교차 →
removeNonOrthogonalPoints가 꼭짓점 축소 → "SK 가 wall.baseLines 안에서 멈추고 확장 안 됨" 증상.
- 단위:
__limited는 cm (value 가 mm→cm 변환된 뒤 delta 로 쓰임).roof.moveUpDown은 mm (movingLineFromSkeleton 내부에서Big(moveUpDown).div(10)으로 cm 환산). 그래서*10.
Why:
- 거부(reject)로 시도했더니 사용자가 평행 위치까지 정확히 가는 정상 케이스에서도 wall 이 안 움직인다고 함 → 거부 정책 폐기.
- 클램핑은 사용자 의도에 가장 근접: "큰 값을 입력해도 알아서 평행까지 멈춘다" 는 UX. 사용자 입장에서 다시 정확한 값을 계산할 필요 없음.
- 사후 처리(silent skip / 오버 좌표로 SK 재빌드 / 오버 전용 함수) 는 invalid geometry 를 시각화하는 한계 때문에 매번 추가 요구가 발생.
- 사전 클램핑으로 wall.baseLines invariant(인접 라인 방향 유지)를 강제하면 하류(SK 빌드, innerLines 생성, lastPoints) 모두 정상 가정 하에 동작.
가정:
- currentLine 은 수평(y1==y2) 또는 수직(x1==x2). 직각 polygon 가정.
- nextLine/prevLine 은 currentLine 과 직각이 아니어도 동작은 하지만, 한계 식의 정밀성은 직각일 때 최적.
- 대각선 라인 polygon 에서 정밀도 이슈 발생 시 한계 식을 일반 벡터 투영으로 확장 필요.
How to apply:
- handleSave 외 다른 wall.baseLines mutation 경로(드래그 이동 등)에도 동일 OVER_GUARD 추가가 일관됨. 현재는 handleSave 에만 적용.
- OVER_GUARD 통과 후 평행이 정확히 일치한 인접 라인은 zero-length baseLine 이 됨 → 기존
SHOULDER_ABSORBED가드(planeSize < 1)로 SK 빌드에서 자연 처리.
Dead code 보존:
- 이전 정책으로 추가된 아래 코드는 남겨둔다 (안전망 + 다른 mutation 경로 추가 가능성):
skeleton-utils.js: drawSkeletonRidgeRoofFromBaseLines함수 (오버 전용 SK 빌드).QPolygon.drawHelpLine의 verdict==='crossed' →drawSkeletonRidgeRoofFromBaseLines분기.skeleton-utils.js: drawSkeletonRidgeRoof내부 crossed 경고 로그.redrawHelpLineForced래퍼.
- 현행 정책에서는 OVER_GUARD 가 막아주므로 위 분기들은 호출되지 않음. 사용자가 명시적으로 제거 요청할 때까지 보존.
규칙 교체 이력:
- 2026-04-22: "리셋 없이 조용히 스킵" (silent skip). Why: 사용자 추가 라인 보존, UI 깜빡임 방지.
- 2026-04-24 (1차): "crossed 가드 제거 + 오버 좌표 그대로 일반 경로 통과". 결과: 일반 경로 확장/클램핑이 좌표 왜곡.
- 2026-04-24 (2차): "crossed 시 오버 전용 함수로 분기". 결과: SK 가 roofLine 바깥으로 삐져나감 → 추가 클립 요구.
- 2026-04-24 (3차): "OVER_GUARD 로 mutation 거부". 결과: 평행 위치 정확히 도달하는 정상 케이스도 거부됨 → 폐기.
- 2026-04-24 (4차): "OVER_GUARD 로 평행 위치 정확히 도달하게 클램핑". 결과: 정확한 평행이 polygon 중복 꼭짓점을 만들어 SK 빌더가 헛선을 추가. 이전 평행 이동이 멀쩡했던 이유는 fabric floating point 노이즈 덕이었음.
- 2026-04-24 (최종 1차): "OVER_GUARD 클램핑 + OVER_EPS=0.5 여유". 평행보다 0.5 모자란 위치까지 이동 → 인접 baseLine 이 0.5 길이로 남고 SHOULDER_ABSORBED(planeSize<1) 가드가 자연 처리. 사용자 체감은 거의 정확한 평행.
- 2026-04-24 (최종 2차 버그 fix): 위 상태에서 "SK 가 확장을 안하고 wall.baseLines 안에서 멈춤" 증상 발생. 원인:
roof.moveUpDown을 원본 입력값(parseInt) 그대로 두어 movingLineFromSkeleton 이 확장을 원본 크기로 시도 → polygon 자기교차. fix: 클램핑 블록 안에서roof.moveUpDown = Math.round(Math.abs(__limited) * 10)동시 갱신.