perf: add spatial filtering to skip distant regions
CRITICAL PERFORMANCE FIX: Skip DOM queries for regions far from cursor using pre-computed region centers and distance check. Problem: - Still iterating ALL ~200 regions on every mouse move - Calling getBoundingClientRect() on each one (expensive DOM query) - Even with early returns, doing 200 DOM queries per frame Solution: - Use pre-computed region.center (already in map data) - Convert center to screen coords using matrixTransform() - Calculate distance from cursor to region center - Skip regions > 150px away BEFORE touching DOM - Only query DOM for nearby regions (~5-10 typically) Performance improvement: - Before: 200 DOM queries per mouse move - After: ~5-10 DOM queries per mouse move (only nearby regions) - 95% reduction in DOM queries Distance threshold: - Detection box is 50px - Using 150px threshold (3× detection box size) - Generous to avoid false negatives for large regions - Distance check uses squared distance (no sqrt needed) This is the proper "broad phase" optimization before expensive checks. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
348ce8f314
commit
8cb4c88bef
|
|
@ -173,7 +173,46 @@ export function useRegionDetection(options: UseRegionDetectionOptions): UseRegio
|
|||
let totalRegionArea = 0
|
||||
let detectedSmallestSize = Infinity
|
||||
|
||||
// Get SVG transformation for converting region centers to screen coords
|
||||
const screenCTM = svgElement.getScreenCTM()
|
||||
if (!screenCTM) {
|
||||
return {
|
||||
detectedRegions: [],
|
||||
regionUnderCursor: null,
|
||||
regionUnderCursorArea: 0,
|
||||
regionsInBox: 0,
|
||||
hasSmallRegion: false,
|
||||
detectedSmallestSize: Infinity,
|
||||
totalRegionArea: 0,
|
||||
}
|
||||
}
|
||||
|
||||
const viewBoxParts = mapData.viewBox.split(' ').map(Number)
|
||||
const viewBoxX = viewBoxParts[0] || 0
|
||||
const viewBoxY = viewBoxParts[1] || 0
|
||||
|
||||
mapData.regions.forEach((region) => {
|
||||
// PERFORMANCE: Quick distance check using pre-computed center
|
||||
// This avoids expensive DOM queries for regions far from cursor
|
||||
// Region center is in SVG coordinates, convert to screen coords
|
||||
const svgCenter = svgElement.createSVGPoint()
|
||||
svgCenter.x = region.center[0]
|
||||
svgCenter.y = region.center[1]
|
||||
const screenCenter = svgCenter.matrixTransform(screenCTM)
|
||||
|
||||
// Calculate rough distance from cursor to region center
|
||||
const dx = screenCenter.x - cursorClientX
|
||||
const dy = screenCenter.y - cursorClientY
|
||||
const distanceSquared = dx * dx + dy * dy
|
||||
|
||||
// Skip regions whose centers are far from the detection box
|
||||
// Use generous threshold to avoid false negatives (detection box is 50px, so check 150px)
|
||||
const MAX_DISTANCE = 150
|
||||
if (distanceSquared > MAX_DISTANCE * MAX_DISTANCE) {
|
||||
return // Region is definitely too far away
|
||||
}
|
||||
|
||||
// Region is close enough - now do proper DOM-based checks
|
||||
const regionPath = svgElement.querySelector(`path[data-region-id="${region.id}"]`)
|
||||
if (!regionPath || !(regionPath instanceof SVGGeometryElement)) return
|
||||
|
||||
|
|
@ -183,7 +222,7 @@ export function useRegionDetection(options: UseRegionDetectionOptions): UseRegio
|
|||
// Sample multiple points within the detection box to check for intersection
|
||||
// This prevents false positives from irregularly shaped regions
|
||||
|
||||
// First quick check: does bounding box even overlap? (fast rejection)
|
||||
// Second check: does bounding box overlap? (fast rejection)
|
||||
const regionLeft = pathRect.left
|
||||
const regionRight = pathRect.right
|
||||
const regionTop = pathRect.top
|
||||
|
|
|
|||
Loading…
Reference in New Issue