import type { BlockModel, PierModel } from './geometry'; /** * 받침 배치 규칙 (승인 2026-10-26). * - C4·C5: 코핑 가장자리(교각축 직각, 폭 B의 두 가장자리)와 받침 중심 사이 거리. 모델값을 쓰고 도면값과 비교한다. * - 일체: 같은 순서끼리 (전열 1 · 후열 1, 전열 2 · 후열 2 …, 각 열의 번호 순) 두 받침의 편심거리가 100 mm를 넘으면 분리를 우선한다. * (P1 은 전열 1–5, 후열 6–10 으로 번호가 매겨져 있어서 번호 자체가 아니라 열 안의 순서로 짝짓는다.) * 편심거리 = 두 받침 중심의 교각축 방향 차이 |s(전열) − s(후열)|. */ export const PAIR_ECCENTRICITY_LIMIT_MM = 100; export interface EdgeDistance { no: number; row: BlockModel['row']; /** 교각축 직각 방향: 코핑 한쪽 가장자리(n = −B/2)에서 받침 중심까지 (mm) = C4. */ leftMm: number; /** 받침 중심에서 코핑 다른 가장자리(n = +B/2)까지 (mm) = C5. */ rightMm: number; } export interface PairEccentricity { /** 전열 번호 and 후열 번호 of the pair. */ frontNo: number; backNo: number; eccentricityMm: number; /** true: 편심이 한계 이하, 일체 배치 가능. */ allowed: boolean; } /** C4·C5 from the model: distances from the two coping edges (±B/2, across the coping width) to each bearing centre (mm). */ export function bearingEdgeDistances(m: PierModel): EdgeDistance[] { const half = m.copingB / 2; return m.blocks.map((b) => ({ no: b.no, row: b.row, leftMm: (b.n + half) * 1000, rightMm: (half - b.n) * 1000, })); } /** * Pairs by order within each row (전열 1 · 후열 1, 2 · 2 …) with their eccentricity. * A bearing left without a partner (the rows have different counts) is not paired. */ export function pairEccentricities(blocks: BlockModel[]): PairEccentricity[] { const byNo = (a: BlockModel, b: BlockModel) => a.no - b.no; const front = blocks.filter((b) => b.row === '전열').sort(byNo); const back = blocks.filter((b) => b.row === '후열').sort(byNo); const out: PairEccentricity[] = []; for (let i = 0; i < Math.min(front.length, back.length); i++) { const e = Math.abs(front[i].s - back[i].s) * 1000; out.push({ frontNo: front[i].no, backNo: back[i].no, eccentricityMm: e, allowed: e <= PAIR_ECCENTRICITY_LIMIT_MM }); } return out; } /** * 일체 is possible when there is at least one pair and every pair is within the limit (승인 2026-10-26). * Returns the pairs over the limit so the reason can be shown. */ export function integratedCheck(blocks: BlockModel[]): { allowed: boolean; overLimit: PairEccentricity[] } { const pairs = pairEccentricities(blocks); const overLimit = pairs.filter((p) => !p.allowed); return { allowed: pairs.length > 0 && overLimit.length === 0, overLimit }; }