qcast-front/.claude/memory/feedback_over_move_silent_skip.md

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) 동시 갱신.