import type { MeshData } from '@ifc-lite/geometry'; import type { Ray, Vec3, Intersection } from './raycaster.js'; export declare enum SnapType { VERTEX = "vertex", EDGE = "edge", FACE = "face", FACE_CENTER = "face_center" } export interface SnapTarget { type: SnapType; position: Vec3; normal?: Vec3; expressId: number; confidence: number; metadata?: { vertices?: Vec3[]; edgeIndex?: number; faceIndex?: number; }; } export interface SnapOptions { snapToVertices: boolean; snapToEdges: boolean; snapToFaces: boolean; snapRadius: number; screenSnapRadius: number; } export interface EdgeLockInput { edge: { v0: Vec3; v1: Vec3; } | null; meshExpressId: number | null; lockStrength: number; } export interface MagneticSnapResult { snapTarget: SnapTarget | null; edgeLock: { edge: { v0: Vec3; v1: Vec3; } | null; meshExpressId: number | null; edgeT: number; shouldLock: boolean; shouldRelease: boolean; isCorner: boolean; cornerValence: number; }; } export declare class SnapDetector { private raycaster; private defaultOptions; private geometryCache; /** * Detect best snap target near cursor */ detectSnapTarget(ray: Ray, meshes: MeshData[], intersection: Intersection | null, camera: { position: Vec3; fov: number; }, screenHeight: number, options?: Partial): SnapTarget | null; /** * Detect snap target with magnetic edge locking behavior * This provides the "stick and slide along edges" experience */ detectMagneticSnap(ray: Ray, meshes: MeshData[], intersection: Intersection | null, camera: { position: Vec3; fov: number; }, screenHeight: number, currentEdgeLock: EdgeLockInput, options?: Partial): MagneticSnapResult; /** * Maintain an existing edge lock - slide along edge or release if moved away */ private maintainEdgeLock; /** * Detect if position is at a corner (vertex with multiple edges) */ private detectCorner; /** * Get or compute geometry cache for a mesh */ private getGeometryCache; /** * Stable cache key for a mesh's snap geometry. * * GPU-instanced occurrences carry an explicit per-occurrence `occurrenceKey` * (issue #1405). Flat meshes do not, and keying them on `expressId` alone is * wrong whenever ONE entity is emitted as several flat sub-pieces — mesh * fragmentation routinely splits an element into many `MeshData` pieces (e.g. * an IfcMechanicalFastener "Bolt assembly" of mapped items materialized as 24 * pieces), and mapped copies share both `expressId` and local positions while * differing only in `origin`. Keying on `expressId` served the first piece's * vertices/edges for every other, so snap lit up on a single piece. * * So flat pieces key on a cheap content signature: `expressId` + per-piece * `origin` (distinguishes same-template copies at different placements) + * buffer sizes + sampled vertices (distinguishes distinct sub-pieces of one * element). Pieces whose world geometry is genuinely identical collapse to the * same key, which is correct (their snap geometry is the same). */ private cacheKeyFor; /** * Find vertices near point */ private findVertices; /** * Find edges near point */ private findEdges; /** * Clear geometry cache (call when meshes change) */ clearCache(): void; /** * Find faces/planes near intersection */ private findFaces; /** * Select best snap target based on confidence and priority */ private getBestSnapTarget; } //# sourceMappingURL=snap-detector.d.ts.map