fix: use screen pixels for zoom, abandon SVG path parsing

The calculateBoundingBox function doesn't handle relative SVG path
commands correctly. Gibraltar's path uses 'm' (relative move) followed
by tiny relative offsets like -0.009, 0.047, but the function treated
them as absolute coordinates, giving width of 460 units instead of 0.08.

Proper SVG path parsing requires:
- Tracking current position as you iterate through commands
- Handling both absolute (M,L,H,V,C,S,Q,T,A) and relative (m,l,h,v,c,s,q,t,a)
- Curves need Bezier math to find actual bounds
- Too complex for this use case

Solution: Use screen pixels (getBoundingClientRect) for everything.
Screen pixel measurements work correctly for tiny regions:
- Gibraltar: 0.08px (accurate)
- Jersey: 0.82px (accurate)

New zoom formula for screen pixels:
- Sub-pixel (< 1px): 1000/(size+0.05)
  - Gibraltar (0.08px): ~7692x → capped at 1000x
- Tiny (1-10px): 500/(size+0.5)
  - 1px: ~333x
  - 5px: ~91x
- Small (10-50px): Linear up to +50x

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Thomas Hallock 2025-11-19 10:53:18 -06:00
parent d72f309bad
commit 912dc385b3
1 changed files with 23 additions and 23 deletions

View File

@ -826,15 +826,13 @@ export function MapRenderer({
}) })
} }
// Track region sizes for adaptive zoom and dampening // Track region sizes - use screen pixels for both zoom and dampening
// - SVG units (from viewBox) for accurate zoom calculation // SVG path parsing is too complex (relative vs absolute commands)
// - Screen pixels (from getBoundingClientRect) for cursor dampening // Screen pixels work well for tiny regions like Gibraltar (0.08px)
const svgBBox = calculateBoundingBox(region.path)
const svgSize = Math.min(svgBBox.width, svgBBox.height)
const screenSize = Math.min(pixelWidth, pixelHeight) const screenSize = Math.min(pixelWidth, pixelHeight)
totalRegionArea += pixelArea totalRegionArea += pixelArea
detectedSmallestSize = Math.min(detectedSmallestSize, svgSize) detectedSmallestSize = Math.min(detectedSmallestSize, screenSize)
detectedSmallestScreenSize = Math.min(detectedSmallestScreenSize, screenSize) detectedSmallestScreenSize = Math.min(detectedSmallestScreenSize, screenSize)
} }
}) })
@ -861,16 +859,15 @@ export function MapRenderer({
// Debug logging - ONLY for Gibraltar or ultra-small regions // Debug logging - ONLY for Gibraltar or ultra-small regions
const hasGibraltar = detectedRegions.includes('gi') const hasGibraltar = detectedRegions.includes('gi')
if (hasGibraltar || detectedSmallestSize < 5) { if (hasGibraltar || detectedSmallestSize < 1) {
console.log( console.log(
`[Magnifier] ${hasGibraltar ? '🎯 GIBRALTAR DETECTED' : '🔍 Tiny region'} Detection:`, `[Magnifier] ${hasGibraltar ? '🎯 GIBRALTAR DETECTED' : '🔍 Tiny region'} Detection:`,
{ {
detectedRegionIds: detectedRegions, detectedRegionIds: detectedRegions,
regionsInBox, regionsInBox,
smallestSvgSize: detectedSmallestSize.toFixed(4) + ' SVG units', smallestScreenSize: detectedSmallestSize.toFixed(4) + 'px',
smallestScreenSize: detectedSmallestScreenSize.toFixed(2) + 'px',
shouldShow, shouldShow,
movementMultiplier: getMovementMultiplier(detectedSmallestScreenSize).toFixed(2), movementMultiplier: getMovementMultiplier(detectedSmallestSize).toFixed(2),
} }
) )
} }
@ -885,21 +882,25 @@ export function MapRenderer({
adaptiveZoom += countFactor * 20 // Up to +20x for density adaptiveZoom += countFactor * 20 // Up to +20x for density
// Add zoom based on smallest region size (tiny regions need EXTREME zoom) // Add zoom based on smallest region size (tiny regions need EXTREME zoom)
// Note: detectedSmallestSize is now in SVG viewBox units (0-1010), not screen pixels // detectedSmallestSize is in screen pixels (from getBoundingClientRect)
// Gibraltar is ~0.08 SVG units, most countries are 10-100 SVG units // Gibraltar is ~0.08px, most countries are 10-100px
let sizeZoom = 0 let sizeZoom = 0
if (detectedSmallestSize !== Infinity) { if (detectedSmallestSize !== Infinity) {
// For Gibraltar (0.08 SVG units): we need massive zoom // For Gibraltar (0.08px): we need massive zoom
// Use exponential scaling for tiny regions // Use exponential scaling for sub-pixel and tiny regions
if (detectedSmallestSize < 5) { if (detectedSmallestSize < 1) {
// Tiny regions (< 5 SVG units) get exponential zoom // Sub-pixel regions get extreme zoom
// Gibraltar (0.08): 200/(0.08+0.1) ≈ 1111x → capped at 1000x // Gibraltar (0.08px): 1000/(0.08+0.05) ≈ 7692x → capped at 1000x
// 1 SVG unit: 200/(1+0.1) ≈ 182x sizeZoom = 1000 / (detectedSmallestSize + 0.05)
// 5 SVG units: 200/(5+0.1) ≈ 39x } else if (detectedSmallestSize < 10) {
sizeZoom = 200 / (detectedSmallestSize + 0.1) // Very small regions (1-10px) get high zoom
// 1px: 500/(1+0.5) ≈ 333x
// 5px: 500/(5+0.5) ≈ 91x
// 10px: 500/(10+0.5) ≈ 48x
sizeZoom = 500 / (detectedSmallestSize + 0.5)
} else { } else {
// Regular small regions use linear scaling // Regular small regions use linear scaling
const sizeFactor = Math.max(0, 1 - detectedSmallestSize / 100) // 0 to 1 const sizeFactor = Math.max(0, 1 - detectedSmallestSize / 50) // 0 to 1
sizeZoom = sizeFactor * 50 // Up to +50x for small regions sizeZoom = sizeFactor * 50 // Up to +50x for small regions
} }
adaptiveZoom += sizeZoom adaptiveZoom += sizeZoom
@ -912,8 +913,7 @@ export function MapRenderer({
// Debug logging for Gibraltar - show full calculation breakdown // Debug logging for Gibraltar - show full calculation breakdown
if (hasGibraltar) { if (hasGibraltar) {
console.log(`[Zoom] 🎯 GIBRALTAR BREAKDOWN:`, { console.log(`[Zoom] 🎯 GIBRALTAR BREAKDOWN:`, {
regionSize: `${detectedSmallestSize.toFixed(6)} SVG units`, regionSize: `${detectedSmallestSize.toFixed(6)}px (screen)`,
viewBoxSize: '1010 × 666',
baseZoom: 10, baseZoom: 10,
densityZoom: (countFactor * 20).toFixed(1), densityZoom: (countFactor * 20).toFixed(1),
sizeZoom: sizeZoom.toFixed(1), sizeZoom: sizeZoom.toFixed(1),