diff --git a/src/hooks/pdf-import/usePdfImport.js b/src/hooks/pdf-import/usePdfImport.js index 01fa4fb2..5fc67f6f 100644 --- a/src/hooks/pdf-import/usePdfImport.js +++ b/src/hooks/pdf-import/usePdfImport.js @@ -7,10 +7,14 @@ import { usePolygon } from '@/hooks/usePolygon' import { useSwal } from '@/hooks/useSwal' import { useMessage } from '@/hooks/useMessage' import { logger } from '@/util/logger' +import { snapNearAxisEdges, cleanSelfIntersectingPolygon } from '@/util/qpolygon-utils' +import * as turf from '@turf/turf' const MM_PER_CANVAS_UNIT = 10 const MAX_DIM = 1500 const MIN_CANVAS_UNIT_LENGTH = 0.5 +const SIMPLIFY_TOLERANCE = 0.5 // canvas unit ≈ 5mm. 직선 위 잉여 정점/미세 엣지 제거(보수적) +const SNAP_ANGLE_TOL_DEG = 2 // 거의 수평/수직 엣지를 정확히 축정렬로 스냅 const toCanvasUnits = (mmPoints) => mmPoints.map((p) => ({ x: p.x / MM_PER_CANVAS_UNIT, y: p.y / MM_PER_CANVAS_UNIT })) @@ -57,6 +61,30 @@ const fitAndCenter = (points, canvas) => { })) } +const simplifyRing = (points, tolerance) => { + if (points.length < 4) return points + try { + const coords = points.map((p) => [p.x, p.y]) + coords.push([points[0].x, points[0].y]) + const simplified = turf.simplify(turf.polygon([coords]), { tolerance, highQuality: true }) + const ring = simplified.geometry.coordinates[0] + const out = ring.slice(0, -1).map((c) => ({ x: c[0], y: c[1] })) + return out.length >= 3 ? out : points + } catch (e) { + logger.warn('[usePdfImport] simplifyRing 실패', e?.message || e) + return points + } +} + +// AI 추출 좌표의 결정적 정규화: 축정렬 스냅 → 과다정점 단순화 → 자기교차 복구. +// 순서 주의 — 스냅(정점 이동) 후 단순화(정점 제거)해야 축정렬이 보존된다. +const normalizeGeometry = (points) => { + let pts = snapNearAxisEdges(points, SNAP_ANGLE_TOL_DEG) + pts = simplifyRing(pts, SIMPLIFY_TOLERANCE) + pts = cleanSelfIntersectingPolygon(pts) + return pts.length >= 3 ? pts : points +} + export function usePdfImport() { const canvas = useRecoilValue(canvasState) const setOuterLinePoints = useSetRecoilState(outerLinePointsState) @@ -133,6 +161,7 @@ export function usePdfImport() { return false } + canvasPoints = normalizeGeometry(canvasPoints) canvasPoints = fitAndCenter(canvasPoints, canvas) canvasPoints = ensureCounterClockwise(canvasPoints)