feat(pdf-import): 외곽선 기하 후처리(축정렬 스냅·단순화·자기교차 복구) 추가
- normalizeGeometry 단계 신설: snapNearAxisEdges(축정렬 스냅) → turf.simplify(과다 정점 단순화) → cleanSelfIntersectingPolygon(자기교차 복구) - applyOuterline의 중복점 제거 직후·fitAndCenter 전에 삽입(스냅→단순화 순서로 축정렬 보존) - 기존 검증 순수함수 재사용, 신규 의존성 없음 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
afda525be9
commit
0418dd84ee
@ -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)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user