From 0dcaabb8a5a7175e511affc2f15196784aeec05a Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Wed, 19 Nov 2025 10:48:49 -0600 Subject: [PATCH] fix: use SVG viewBox units instead of screen pixels for zoom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous implementation used getBoundingClientRect() which returns axis-aligned bounding boxes in screen pixels. This has two problems: 1. **Inaccurate sizing**: Bounding boxes for irregular/rotated shapes are much larger than the actual visible area 2. **Floating-point precision**: Sub-pixel screen measurements lose precision and cause most regions to trigger max zoom Solution: Use SVG path bounding box from viewBox coordinates The world map viewBox is 1010 × 666 SVG units: - Gibraltar: ~0.08 SVG units (extremely tiny!) - Jersey: ~0.5 SVG units - Most countries: 10-100 SVG units New zoom formula for SVG units: - Tiny regions (< 5 units): 200 / (size + 0.1) - Gibraltar (0.08): 200/0.18 ≈ 1111x → capped at 1000x - 1 unit: 200/1.1 ≈ 182x - 5 units: 200/5.1 ≈ 39x - Small regions (5-100 units): Linear scaling up to +50x - Large regions (>100 units): Minimal size-based zoom This avoids floating-point precision issues and gives accurate zoom levels based on the region's true SVG path dimensions. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/MapRenderer.tsx | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx b/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx index c56a01d5..1267f3e4 100644 --- a/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx +++ b/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx @@ -826,9 +826,13 @@ export function MapRenderer({ } // Track region sizes for adaptive zoom and dampening + // Use SVG path bounding box (from viewBox coordinates) for more accurate sizing + // getBoundingClientRect() gives axis-aligned boxes which are too large for irregular shapes + const svgBBox = calculateBoundingBox(region.path) + const svgSize = Math.min(svgBBox.width, svgBBox.height) + totalRegionArea += pixelArea - const regionSize = Math.min(pixelWidth, pixelHeight) - detectedSmallestSize = Math.min(detectedSmallestSize, regionSize) + detectedSmallestSize = Math.min(detectedSmallestSize, svgSize) } }) @@ -877,18 +881,22 @@ export function MapRenderer({ adaptiveZoom += countFactor * 20 // Up to +20x for density // 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 + // Gibraltar is ~0.08 SVG units, most countries are 10-100 SVG units let sizeZoom = 0 if (detectedSmallestSize !== Infinity) { - // For Gibraltar (0.08px): we need massive zoom - // Use exponential scaling for sub-pixel regions - if (detectedSmallestSize < 1) { - // Sub-pixel regions get exponential zoom - // 0.08px → ~970x, 0.5px → ~180x, 1px → ~95x - sizeZoom = 1000 / (detectedSmallestSize + 0.05) + // For Gibraltar (0.08 SVG units): we need massive zoom + // Use exponential scaling for tiny regions + if (detectedSmallestSize < 5) { + // Tiny regions (< 5 SVG units) get exponential zoom + // Gibraltar (0.08): 200/(0.08+0.1) ≈ 1111x → capped at 1000x + // 1 SVG unit: 200/(1+0.1) ≈ 182x + // 5 SVG units: 200/(5+0.1) ≈ 39x + sizeZoom = 200 / (detectedSmallestSize + 0.1) } else { // Regular small regions use linear scaling - const sizeFactor = Math.max(0, 1 - detectedSmallestSize / 20) // 0 to 1 - sizeZoom = sizeFactor * 150 // Up to +150x for small regions + const sizeFactor = Math.max(0, 1 - detectedSmallestSize / 100) // 0 to 1 + sizeZoom = sizeFactor * 50 // Up to +50x for small regions } adaptiveZoom += sizeZoom } @@ -900,7 +908,8 @@ export function MapRenderer({ // Debug logging for Gibraltar - show full calculation breakdown if (hasGibraltar) { console.log(`[Zoom] 🎯 GIBRALTAR BREAKDOWN:`, { - regionSize: `${detectedSmallestSize.toFixed(4)}px`, + regionSize: `${detectedSmallestSize.toFixed(6)} SVG units`, + viewBoxSize: '1010 × 666', baseZoom: 10, densityZoom: (countFactor * 20).toFixed(1), sizeZoom: sizeZoom.toFixed(1),