feat(roof-shape): 지붕형상 패턴 매처 matchRoofShapePattern 추가

[작업내용] :
- descriptor(type/ridge/flows)→shapeNum|null 순수 매칭 함수
- flows 집합 1차 키 + type/ridge guard(모순 거부, 결측 관대)
- 폴백 null(autoMatchable=false인 4 비반환), confidence 무시
- Gemini 분석 확장(B)·자동 클릭 배선(C)은 다음 작업으로 분리

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
sangwook yoo 2026-06-24 13:54:15 +09:00
parent cfd8e3bdef
commit bcdd630a00

View File

@ -104,3 +104,25 @@ export const ROOF_SHAPE_PATTERNS = [
// shapeNum 으로 카탈로그 항목 조회. 없으면 null. // shapeNum 으로 카탈로그 항목 조회. 없으면 null.
export const getRoofShapePatternByShapeNum = (shapeNum) => ROOF_SHAPE_PATTERNS.find((pattern) => pattern.shapeNum === shapeNum) || null export const getRoofShapePatternByShapeNum = (shapeNum) => ROOF_SHAPE_PATTERNS.find((pattern) => pattern.shapeNum === shapeNum) || null
// descriptor(RoofShapeDescriptor) → 매칭되는 shapeNum | null.
// 매칭 키: flows 집합(1차) + type/ridge guard(모순 거부). confidence 는 무시(임계값 정책은 호출자).
// 실패·모순·무효 입력은 모두 null — autoMatchable:false(shapeNum 4)는 절대 반환하지 않는다.
export const matchRoofShapePattern = (descriptor) => {
if (!descriptor || typeof descriptor !== 'object') return null
const { type, ridge, flows } = descriptor
if (!Array.isArray(flows)) return null
const FLOW_VALUES = Object.values(ROOF_FLOW)
if (flows.some((flow) => !FLOW_VALUES.includes(flow))) return null
const flowKey = JSON.stringify([...new Set(flows)].sort())
const candidate = ROOF_SHAPE_PATTERNS.find((pattern) => pattern.autoMatchable && JSON.stringify([...pattern.flows].sort()) === flowKey)
if (!candidate) return null
// type/ridge 가 주어졌는데 후보와 모순이면 거부(결측이면 flows 만으로 통과 — 관대)
if (type && type !== candidate.type) return null
if (ridge && ridge !== candidate.ridge) return null
return candidate.shapeNum
}