From e900e4465bbde8d8b0787581d6e8603fdf36975f Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Tue, 18 Nov 2025 16:14:21 -0600 Subject: [PATCH] fix: properly zoom to selected continent in game phases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix continent zoom to work in Study and Playing phases (not setup): Fixed Bounding Box Calculation: - Rewrite calculateBoundingBox() to properly parse SVG path commands - Handle both absolute (M, L, H, V, C, Q) and relative (m, l, h, v, c, q) commands - Track current position for accurate relative coordinate conversion - Include all control points for Bezier curves in bounding box - Now correctly calculates continent boundaries Reverted Setup Screen Changes: - Remove zoom from ContinentSelector (keep full world visible) - Setup screen shows complete world map for easy continent selection - Zoom only happens in game phases (Study, Playing, Results) Game Phase Zoom: - Study and Playing phases now properly zoom to selected continent - Uses accurate bounding box from fixed calculateBoundingBox() - 10% padding around continent for optimal visibility - Filtered regions AND adjusted viewBox work together How It Works: 1. User selects continent in setup (sees full world map) 2. Clicks "Start Study & Play" or "Start Game" 3. Study/Playing phases automatically zoom to show only that continent 4. Countries fill the available space for easier clicking 5. Geographic details much more visible Technical Fix: - Previous implementation naively extracted numbers from SVG paths - Didn't account for relative vs absolute coordinates - Resulted in incorrect bounding boxes - New implementation mirrors calculatePathCenter() logic - Properly handles all SVG path command types 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/ContinentSelector.tsx | 11 +- .../src/arcade-games/know-your-world/maps.ts | 178 ++++++++++++++++-- 2 files changed, 170 insertions(+), 19 deletions(-) diff --git a/apps/web/src/arcade-games/know-your-world/components/ContinentSelector.tsx b/apps/web/src/arcade-games/know-your-world/components/ContinentSelector.tsx index c6101121..ed1c4778 100644 --- a/apps/web/src/arcade-games/know-your-world/components/ContinentSelector.tsx +++ b/apps/web/src/arcade-games/know-your-world/components/ContinentSelector.tsx @@ -1,9 +1,9 @@ 'use client' -import { useState, useMemo } from 'react' +import { useState } from 'react' import { css } from '@styled/css' import { useTheme } from '@/contexts/ThemeContext' -import { WORLD_MAP, calculateContinentViewBox } from '../maps' +import { WORLD_MAP } from '../maps' import { getContinentForCountry, CONTINENTS, type ContinentId } from '../continents' import { getRegionColor } from '../mapColors' @@ -78,11 +78,6 @@ export function ContinentSelector({ return 0.3 } - // Calculate viewBox based on selected continent - const viewBox = useMemo(() => { - return calculateContinentViewBox(WORLD_MAP.regions, selectedContinent, WORLD_MAP.viewBox) - }, [selectedContinent]) - return (
= 2) { + currentX = params[0] + currentY = params[1] + minX = Math.min(minX, currentX) + maxX = Math.max(maxX, currentX) + minY = Math.min(minY, currentY) + maxY = Math.max(maxY, currentY) + } + break + + case 'm': // Move to (relative) + if (params.length >= 2) { + currentX += params[0] + currentY += params[1] + minX = Math.min(minX, currentX) + maxX = Math.max(maxX, currentX) + minY = Math.min(minY, currentY) + maxY = Math.max(maxY, currentY) + } + break + + case 'L': // Line to (absolute) + for (let i = 0; i < params.length - 1; i += 2) { + currentX = params[i] + currentY = params[i + 1] + minX = Math.min(minX, currentX) + maxX = Math.max(maxX, currentX) + minY = Math.min(minY, currentY) + maxY = Math.max(maxY, currentY) + } + break + + case 'l': // Line to (relative) + for (let i = 0; i < params.length - 1; i += 2) { + currentX += params[i] + currentY += params[i + 1] + minX = Math.min(minX, currentX) + maxX = Math.max(maxX, currentX) + minY = Math.min(minY, currentY) + maxY = Math.max(maxY, currentY) + } + break + + case 'H': // Horizontal line (absolute) + for (const x of params) { + currentX = x + minX = Math.min(minX, currentX) + maxX = Math.max(maxX, currentX) + } + break + + case 'h': // Horizontal line (relative) + for (const dx of params) { + currentX += dx + minX = Math.min(minX, currentX) + maxX = Math.max(maxX, currentX) + } + break + + case 'V': // Vertical line (absolute) + for (const y of params) { + currentY = y + minY = Math.min(minY, currentY) + maxY = Math.max(maxY, currentY) + } + break + + case 'v': // Vertical line (relative) + for (const dy of params) { + currentY += dy + minY = Math.min(minY, currentY) + maxY = Math.max(maxY, currentY) + } + break + + case 'C': // Cubic Bezier (absolute) + for (let i = 0; i < params.length - 1; i += 6) { + if (i + 5 < params.length) { + // Check all control points and endpoint + for (let j = 0; j < 6; j += 2) { + const x = params[i + j] + const y = params[i + j + 1] + minX = Math.min(minX, x) + maxX = Math.max(maxX, x) + minY = Math.min(minY, y) + maxY = Math.max(maxY, y) + } + currentX = params[i + 4] + currentY = params[i + 5] + } + } + break + + case 'c': // Cubic Bezier (relative) + for (let i = 0; i < params.length - 1; i += 6) { + if (i + 5 < params.length) { + // Check all control points and endpoint (converted to absolute) + for (let j = 0; j < 6; j += 2) { + const x = currentX + params[i + j] + const y = currentY + params[i + j + 1] + minX = Math.min(minX, x) + maxX = Math.max(maxX, x) + minY = Math.min(minY, y) + maxY = Math.max(maxY, y) + } + currentX += params[i + 4] + currentY += params[i + 5] + } + } + break + + case 'Q': // Quadratic Bezier (absolute) + for (let i = 0; i < params.length - 1; i += 4) { + if (i + 3 < params.length) { + // Check control point and endpoint + for (let j = 0; j < 4; j += 2) { + const x = params[i + j] + const y = params[i + j + 1] + minX = Math.min(minX, x) + maxX = Math.max(maxX, x) + minY = Math.min(minY, y) + maxY = Math.max(maxY, y) + } + currentX = params[i + 2] + currentY = params[i + 3] + } + } + break + + case 'q': // Quadratic Bezier (relative) + for (let i = 0; i < params.length - 1; i += 4) { + if (i + 3 < params.length) { + // Check control point and endpoint (converted to absolute) + for (let j = 0; j < 4; j += 2) { + const x = currentX + params[i + j] + const y = currentY + params[i + j + 1] + minX = Math.min(minX, x) + maxX = Math.max(maxX, x) + minY = Math.min(minY, y) + maxY = Math.max(maxY, y) + } + currentX += params[i + 2] + currentY += params[i + 3] + } + } + break + + case 'Z': + case 'z': + // Close path - no new point needed + break + } } }