// Package seeddefs holds the canonical seed definitions for tracks and // cars. It lives in its own package (no postgres dependencies) so both // the catalog tests and the scripts/genseed SQL generator can consume // the same source-of-truth data. package seeddefs import "math" // Default tracks and cars shipped with the PoC. All entries are // visibility=system and therefore immutable from the API. // // Tracks: five real F1 circuits (2024 calendar), simplified to fit a // 4.5 × 3.2 m bounding box at 1/27 scale. Each track keeps a // recognisable shape and characteristic corners. // // Cars: five real F1 2024 teams. Length / width / weight are real // (1/24 scale), power figures are real (full-scale 1.6L V6 hybrid // turbo, ~1000 hp combined MGU-K + ICE). func DefaultTracks() []Track { return []Track{ Monaco(), BarcelonaCatalunya(), AustriaRedBullRing(), GreatBritainSilverstone(), BelgiumSpa(), } } func DefaultCars() []Car { return []Car{ RedBullRB20(), FerrariSF24(), McLarenMCL38(), MercedesW15(), AstonMartinAMR24(), } } // --------------------------------------------------------------------------- // Tracks // // Coordinates: lower-left origin, metres. Bounds must fit inside a // 4.5 × 3.2 m room. // // Conventions: // - closed loop, samples[i] and samples[0] are not duplicated // - speed_ms is a suggested cornering speed (lower = tighter) // - curvature = 1/radius; 0 = straight // --------------------------------------------------------------------------- // Monaco — narrow street circuit, hairpins, tight corners. // Length ≈ 3.337 km IRL → simplified to 4.0 × 2.6 m. func Monaco() Track { const ( lengthM = 4.0 widthM = 2.6 ) // Famous corners: Sainte-Devote, Casino Square, Mirabeau, Loews hairpin, // Portier, Tunnel, Nouvelle chicane, Tabac, Swimming pool, Rascasse, Anthony Noghes. // Lower speeds through Monaco → 1.8..3.2 m/s. wps := []Waypoint{ // Start/finish straight (Sainte-Devote approach). {X: 0.4, Y: 2.2, HeadingRad: 0, SpeedMs: 3.2, Curvature: 0}, {X: 1.1, Y: 2.2, HeadingRad: 0, SpeedMs: 3.0, Curvature: 0}, // Sainte-Devote (right kink). {X: 1.5, Y: 2.05, HeadingRad: -0.4, SpeedMs: 2.4, Curvature: 1.2}, {X: 1.7, Y: 1.85, HeadingRad: -0.9, SpeedMs: 2.2, Curvature: 1.8}, // Up the hill to Casino / Massenet. {X: 1.55, Y: 1.55, HeadingRad: -1.6, SpeedMs: 2.0, Curvature: 1.4}, {X: 1.25, Y: 1.4, HeadingRad: -2.4, SpeedMs: 2.1, Curvature: 1.2}, // Casino Square (slow left). {X: 0.95, Y: 1.55, HeadingRad: math.Pi, SpeedMs: 1.9, Curvature: 1.6}, // Mirabeau hairpin (right). {X: 0.75, Y: 1.85, HeadingRad: 2.0, SpeedMs: 1.8, Curvature: 2.2}, // Loews hairpin (tight left). {X: 0.95, Y: 2.15, HeadingRad: 2.6, SpeedMs: 1.8, Curvature: 2.6}, {X: 1.2, Y: 2.4, HeadingRad: -2.8, SpeedMs: 2.1, Curvature: 1.4}, // Portier (right kink onto the straight). {X: 1.6, Y: 2.5, HeadingRad: -3.0, SpeedMs: 2.4, Curvature: 1.0}, // Tunnel straight. {X: 2.6, Y: 2.5, HeadingRad: 0, SpeedMs: 3.2, Curvature: 0}, {X: 3.4, Y: 2.5, HeadingRad: 0, SpeedMs: 3.2, Curvature: 0}, // Nouvelle chicane (fast left-right). {X: 3.6, Y: 2.25, HeadingRad: -0.5, SpeedMs: 2.6, Curvature: 1.5}, {X: 3.55, Y: 1.95, HeadingRad: 0.4, SpeedMs: 2.6, Curvature: 1.5}, // Tabac (right). {X: 3.3, Y: 1.75, HeadingRad: 1.0, SpeedMs: 2.3, Curvature: 1.4}, // Swimming pool (left-right-left). {X: 2.95, Y: 1.55, HeadingRad: 1.7, SpeedMs: 2.1, Curvature: 1.8}, {X: 2.6, Y: 1.65, HeadingRad: 2.6, SpeedMs: 2.1, Curvature: 1.8}, {X: 2.4, Y: 1.9, HeadingRad: -2.7, SpeedMs: 2.3, Curvature: 1.4}, // Rascasse (left). {X: 2.4, Y: 2.2, HeadingRad: -3.0, SpeedMs: 2.2, Curvature: 1.6}, // Anthony Noghes (left onto pit straight). {X: 2.6, Y: 2.4, HeadingRad: 2.8, SpeedMs: 2.5, Curvature: 1.0}, // Pit straight (back to start). {X: 3.2, Y: 2.4, HeadingRad: 0, SpeedMs: 3.2, Curvature: 0}, {X: 3.7, Y: 2.4, HeadingRad: 0, SpeedMs: 3.2, Curvature: 0}, {X: 3.95, Y: 2.3, HeadingRad: -0.3, SpeedMs: 2.8, Curvature: 0.8}, } b := ComputeBounds(wps) return Track{ ID: "monaco", Name: "Monaco", Description: "Circuit de Monaco. Narrow street circuit, tight hairpins, no room for error.", AuthorID: "system", Visibility: "system", Scale: 27, LengthM: b.LengthM, WidthM: b.WidthM, LaneWidthM: 0.18, Surface: "tile", Tags: []string{"f1", "street", "tight", "slow", "monaco"}, Centerline: wps, Bounds: b, BestLapHolder: "Verstappen", } } // BarcelonaCatalunya — mix of high-speed and technical, used for testing. // Length ≈ 4.675 km IRL → 4.3 × 2.9 m. func BarcelonaCatalunya() Track { const ( lengthM = 4.3 widthM = 2.9 ) wps := []Waypoint{ // Main straight. {X: 0.3, Y: 0.4, HeadingRad: 0, SpeedMs: 4.5, Curvature: 0}, {X: 1.4, Y: 0.4, HeadingRad: 0, SpeedMs: 4.5, Curvature: 0}, // Turn 1 — Elf (right hairpin). {X: 1.7, Y: 0.65, HeadingRad: math.Pi / 2, SpeedMs: 2.2, Curvature: 1.6}, {X: 1.5, Y: 0.95, HeadingRad: math.Pi, SpeedMs: 2.0, Curvature: 2.0}, // Turn 2-3 — right kink. {X: 1.2, Y: 1.1, HeadingRad: math.Pi + 0.3, SpeedMs: 2.6, Curvature: 1.0}, {X: 0.95, Y: 1.0, HeadingRad: math.Pi + 0.9, SpeedMs: 2.6, Curvature: 1.0}, // Turn 4 — Repsol (left). {X: 0.8, Y: 1.2, HeadingRad: -math.Pi / 2, SpeedMs: 2.4, Curvature: 1.2}, // Turn 5 — fast right. {X: 1.05, Y: 1.5, HeadingRad: 0, SpeedMs: 3.2, Curvature: 0.7}, // Turn 7-8 — slow left-right. {X: 1.3, Y: 1.65, HeadingRad: math.Pi / 2, SpeedMs: 2.4, Curvature: 1.0}, {X: 1.15, Y: 1.85, HeadingRad: math.Pi, SpeedMs: 2.4, Curvature: 1.0}, // Turn 9-10 — Campsa (fast right). {X: 1.4, Y: 2.0, HeadingRad: math.Pi - 0.5, SpeedMs: 3.0, Curvature: 0.7}, {X: 1.7, Y: 2.0, HeadingRad: math.Pi - 1.0, SpeedMs: 3.0, Curvature: 0.7}, // Turn 12-13 — long right. {X: 1.95, Y: 2.2, HeadingRad: math.Pi / 2, SpeedMs: 2.8, Curvature: 0.5}, {X: 1.65, Y: 2.5, HeadingRad: 0.3, SpeedMs: 2.8, Curvature: 0.5}, // Turn 14-15 — fast right curve. {X: 2.2, Y: 2.7, HeadingRad: 0.7, SpeedMs: 3.2, Curvature: 0.5}, {X: 2.8, Y: 2.7, HeadingRad: 1.1, SpeedMs: 3.4, Curvature: 0.5}, // Turn 16 — La Caixa (right hairpin). {X: 3.1, Y: 2.5, HeadingRad: math.Pi / 2 + 0.4, SpeedMs: 2.2, Curvature: 1.4}, {X: 2.95, Y: 2.2, HeadingRad: math.Pi + 0.4, SpeedMs: 2.2, Curvature: 1.4}, // Back straight. {X: 3.3, Y: 1.9, HeadingRad: 0, SpeedMs: 4.0, Curvature: 0}, {X: 4.0, Y: 1.9, HeadingRad: 0, SpeedMs: 4.0, Curvature: 0}, // Turn 16bis-17 (chicane before start). {X: 4.05, Y: 1.6, HeadingRad: -math.Pi / 2, SpeedMs: 2.4, Curvature: 1.4}, {X: 3.8, Y: 1.4, HeadingRad: math.Pi, SpeedMs: 2.4, Curvature: 1.4}, {X: 3.5, Y: 1.5, HeadingRad: math.Pi + math.Pi/4, SpeedMs: 2.6, Curvature: 1.0}, // Approach to start. {X: 3.1, Y: 1.25, HeadingRad: -math.Pi / 2 - 0.4, SpeedMs: 3.0, Curvature: 0.7}, {X: 2.7, Y: 1.0, HeadingRad: -math.Pi / 2 - 0.9, SpeedMs: 3.0, Curvature: 0.7}, {X: 2.3, Y: 0.7, HeadingRad: math.Pi + 0.4, SpeedMs: 3.4, Curvature: 0.5}, {X: 1.6, Y: 0.5, HeadingRad: math.Pi + 0.2, SpeedMs: 3.6, Curvature: 0.4}, {X: 0.9, Y: 0.42, HeadingRad: 0, SpeedMs: 4.0, Curvature: 0}, } b := ComputeBounds(wps) return Track{ ID: "barcelona-catalunya", Name: "Barcelona-Catalunya", Description: "Circuit de Barcelona-Catalunya. Mix of high-speed corners and technical chicanes.", AuthorID: "system", Visibility: "system", Scale: 27, LengthM: b.LengthM, WidthM: b.WidthM, LaneWidthM: 0.18, Surface: "tile", Tags: []string{"f1", "balanced", "testing", "barcelona"}, Centerline: wps, Bounds: b, BestLapHolder: "Verstappen", } } // AustriaRedBullRing — short, fast, lots of elevation IRL. // Length ≈ 4.318 km IRL → 4.0 × 2.4 m. func AustriaRedBullRing() Track { const ( lengthM = 4.0 widthM = 2.4 ) wps := []Waypoint{ // Main straight. {X: 0.3, Y: 0.4, HeadingRad: 0, SpeedMs: 4.8, Curvature: 0}, {X: 1.6, Y: 0.4, HeadingRad: 0, SpeedMs: 4.8, Curvature: 0}, // Turn 1 — right hairpin. {X: 1.85, Y: 0.65, HeadingRad: math.Pi / 2, SpeedMs: 2.0, Curvature: 1.8}, {X: 1.6, Y: 0.95, HeadingRad: math.Pi, SpeedMs: 2.0, Curvature: 1.8}, // Turn 2 — fast right. {X: 1.3, Y: 1.0, HeadingRad: math.Pi + 0.5, SpeedMs: 3.0, Curvature: 0.7}, // Turn 3 — left. {X: 1.05, Y: 1.2, HeadingRad: -math.Pi / 2, SpeedMs: 2.6, Curvature: 1.0}, // Turn 4 — Remus (fast uphill right). {X: 1.4, Y: 1.4, HeadingRad: 0, SpeedMs: 3.2, Curvature: 0.5}, {X: 1.85, Y: 1.4, HeadingRad: math.Pi / 2 + 0.2, SpeedMs: 3.2, Curvature: 0.5}, // Turn 5-6 — fast right-left. {X: 2.1, Y: 1.65, HeadingRad: math.Pi, SpeedMs: 3.0, Curvature: 0.7}, {X: 2.0, Y: 1.85, HeadingRad: -math.Pi / 2, SpeedMs: 3.0, Curvature: 0.7}, // Turn 7 — right hairpin. {X: 2.25, Y: 2.05, HeadingRad: 0, SpeedMs: 2.0, Curvature: 1.8}, {X: 2.4, Y: 2.0, HeadingRad: math.Pi / 2, SpeedMs: 2.0, Curvature: 1.8}, // Turn 8 — left hairpin. {X: 2.4, Y: 2.2, HeadingRad: math.Pi, SpeedMs: 2.0, Curvature: 1.8}, {X: 2.2, Y: 2.3, HeadingRad: -math.Pi / 2, SpeedMs: 2.0, Curvature: 1.8}, // Turn 9 — back straight start (left kink). {X: 2.55, Y: 2.4, HeadingRad: -0.2, SpeedMs: 3.4, Curvature: 0.4}, {X: 3.0, Y: 2.4, HeadingRad: 0, SpeedMs: 4.0, Curvature: 0}, {X: 3.5, Y: 2.4, HeadingRad: 0, SpeedMs: 4.5, Curvature: 0}, // Turn 10 — fast right. {X: 3.85, Y: 2.15, HeadingRad: math.Pi / 2 + 0.2, SpeedMs: 3.4, Curvature: 0.6}, // Back to pit straight via Schumacher esses. {X: 3.6, Y: 1.8, HeadingRad: math.Pi, SpeedMs: 3.0, Curvature: 0.8}, {X: 3.25, Y: 1.85, HeadingRad: math.Pi + 0.6, SpeedMs: 3.0, Curvature: 0.8}, {X: 3.0, Y: 1.6, HeadingRad: -math.Pi / 2 - 0.4, SpeedMs: 3.2, Curvature: 0.6}, {X: 2.6, Y: 1.4, HeadingRad: -math.Pi, SpeedMs: 3.4, Curvature: 0.5}, {X: 2.0, Y: 1.2, HeadingRad: math.Pi - 0.5, SpeedMs: 3.6, Curvature: 0.4}, {X: 1.4, Y: 0.8, HeadingRad: math.Pi - 0.2, SpeedMs: 3.8, Curvature: 0.3}, {X: 0.8, Y: 0.5, HeadingRad: 0, SpeedMs: 4.2, Curvature: 0.2}, } b := ComputeBounds(wps) return Track{ ID: "austria-redbull-ring", Name: "Austria (Red Bull Ring)", Description: "Red Bull Ring. Short, fast, three straights separated by hairpins.", AuthorID: "system", Visibility: "system", Scale: 27, LengthM: b.LengthM, WidthM: b.WidthM, LaneWidthM: 0.18, Surface: "wood", Tags: []string{"f1", "short", "fast", "austria"}, Centerline: wps, Bounds: b, BestLapHolder: "Verstappen", } } // GreatBritainSilverstone — fast, flowing, essex corners. // Length ≈ 5.891 km IRL → 4.4 × 3.0 m. func GreatBritainSilverstone() Track { const ( lengthM = 4.4 widthM = 3.0 ) wps := []Waypoint{ // Main straight (Wellington / pit straight), bottom of room. {X: 0.3, Y: 0.3, HeadingRad: 0, SpeedMs: 5.0, Curvature: 0}, {X: 2.4, Y: 0.3, HeadingRad: 0, SpeedMs: 5.0, Curvature: 0}, // Turn 1 — Abbey (right). {X: 2.8, Y: 0.45, HeadingRad: math.Pi / 2, SpeedMs: 3.0, Curvature: 0.8}, // Turn 2 — Farm Curve (left). {X: 2.6, Y: 0.8, HeadingRad: math.Pi, SpeedMs: 3.4, Curvature: 0.6}, // Turn 3 — Village (right kink). {X: 2.95, Y: 1.05, HeadingRad: math.Pi / 2 + 0.5, SpeedMs: 3.2, Curvature: 0.7}, // Turn 4 — The Loop (right hairpin). {X: 3.3, Y: 1.3, HeadingRad: 0, SpeedMs: 2.2, Curvature: 1.6}, {X: 3.6, Y: 1.15, HeadingRad: -math.Pi / 2, SpeedMs: 2.2, Curvature: 1.6}, // Turn 5 — Aintree (fast left). {X: 3.6, Y: 0.8, HeadingRad: -math.Pi, SpeedMs: 3.0, Curvature: 0.7}, // Turn 6 — Brooklands (right). {X: 3.85, Y: 0.55, HeadingRad: -math.Pi / 2, SpeedMs: 2.6, Curvature: 1.0}, // Turn 7 — Luffield (long right). {X: 3.55, Y: 0.25, HeadingRad: 0, SpeedMs: 2.4, Curvature: 1.0}, {X: 3.25, Y: 0.25, HeadingRad: math.Pi / 2, SpeedMs: 2.4, Curvature: 1.0}, // Turn 8-9 — Woodcote (right kink onto pit straight). {X: 3.05, Y: 0.55, HeadingRad: math.Pi / 2 + 0.3, SpeedMs: 3.4, Curvature: 0.5}, {X: 2.85, Y: 0.45, HeadingRad: math.Pi, SpeedMs: 4.0, Curvature: 0.3}, // (mid-pit straight into Copse corner) {X: 1.8, Y: 0.3, HeadingRad: 0, SpeedMs: 4.6, Curvature: 0}, // Copse (fast right). {X: 1.2, Y: 0.45, HeadingRad: -math.Pi / 2 - 0.2, SpeedMs: 3.8, Curvature: 0.4}, // Maggotts (right curve, swept). {X: 1.0, Y: 0.85, HeadingRad: -math.Pi - 0.4, SpeedMs: 3.6, Curvature: 0.5}, // Beckets (left hairpin-ish). {X: 0.7, Y: 1.15, HeadingRad: math.Pi / 2 + 0.3, SpeedMs: 2.4, Curvature: 1.2}, {X: 0.5, Y: 0.9, HeadingRad: 0, SpeedMs: 2.6, Curvature: 0.9}, // Chapel (right kink). {X: 0.7, Y: 0.65, HeadingRad: -math.Pi / 2, SpeedMs: 3.0, Curvature: 0.7}, // Hangar (left). {X: 0.45, Y: 0.45, HeadingRad: -math.Pi - 0.4, SpeedMs: 2.6, Curvature: 1.0}, // Stowe (right). {X: 0.75, Y: 1.0, HeadingRad: math.Pi / 2 - 0.4, SpeedMs: 2.4, Curvature: 1.0}, // Vale (left kink). {X: 0.5, Y: 1.5, HeadingRad: -math.Pi - 0.4, SpeedMs: 2.6, Curvature: 0.8}, // Club (right kink onto Wellington). {X: 0.3, Y: 1.15, HeadingRad: -math.Pi / 2, SpeedMs: 2.4, Curvature: 1.0}, // Top return straight + kink back to Wellington. {X: 0.7, Y: 1.6, HeadingRad: math.Pi / 4, SpeedMs: 3.4, Curvature: 0.5}, {X: 2.0, Y: 1.95, HeadingRad: math.Pi / 2 + 0.4, SpeedMs: 3.0, Curvature: 0.7}, {X: 3.0, Y: 2.4, HeadingRad: math.Pi + 0.2, SpeedMs: 3.4, Curvature: 0.4}, {X: 2.4, Y: 2.65, HeadingRad: -math.Pi / 2 - 0.3, SpeedMs: 4.0, Curvature: 0.3}, {X: 1.4, Y: 2.6, HeadingRad: -math.Pi, SpeedMs: 4.4, Curvature: 0.2}, {X: 0.5, Y: 2.3, HeadingRad: -math.Pi + 0.3, SpeedMs: 4.8, Curvature: 0.2}, {X: 0.3, Y: 1.7, HeadingRad: -math.Pi / 2 + 0.3, SpeedMs: 5.0, Curvature: 0.1}, } b := ComputeBounds(wps) return Track{ ID: "great-britain-silverstone", Name: "Great Britain (Silverstone)", Description: "Silverstone Circuit. Fast, flowing corners; home of British motorsport.", AuthorID: "system", Visibility: "system", Scale: 27, LengthM: b.LengthM, WidthM: b.WidthM, LaneWidthM: 0.18, Surface: "carpet", Tags: []string{"f1", "fast", "flowing", "silverstone"}, Centerline: wps, Bounds: b, BestLapHolder: "Hamilton", } } // BelgiumSpa — long, fast, Eau Rouge / Raidillon, varied weather. // Length ≈ 7.004 km IRL → 4.5 × 3.0 m. func BelgiumSpa() Track { const ( lengthM = 4.5 widthM = 3.0 ) wps := []Waypoint{ // Main straight (La Source exit). {X: 0.4, Y: 0.5, HeadingRad: 0, SpeedMs: 4.8, Curvature: 0}, {X: 1.6, Y: 0.5, HeadingRad: 0, SpeedMs: 4.8, Curvature: 0}, // Eau Rouge / Raidillon (fast uphill left-right-left). {X: 1.95, Y: 0.6, HeadingRad: 0.3, SpeedMs: 4.2, Curvature: 0.5}, {X: 2.2, Y: 0.95, HeadingRad: 0.9, SpeedMs: 4.0, Curvature: 0.6}, {X: 2.1, Y: 1.3, HeadingRad: math.Pi + 0.4, SpeedMs: 4.0, Curvature: 0.6}, // Kemmel straight. {X: 2.7, Y: 1.5, HeadingRad: 0, SpeedMs: 5.0, Curvature: 0}, {X: 3.5, Y: 1.5, HeadingRad: 0, SpeedMs: 5.0, Curvature: 0}, // Les Combes (chicane). {X: 3.85, Y: 1.3, HeadingRad: -math.Pi / 2, SpeedMs: 2.4, Curvature: 1.4}, {X: 3.6, Y: 1.1, HeadingRad: math.Pi + 0.3, SpeedMs: 2.6, Curvature: 1.2}, {X: 3.3, Y: 1.25, HeadingRad: math.Pi + 0.9, SpeedMs: 2.6, Curvature: 1.2}, // Malmedy (fast right sweep). {X: 3.5, Y: 1.55, HeadingRad: math.Pi / 2, SpeedMs: 3.6, Curvature: 0.4}, // Rivage (right hairpin). {X: 3.25, Y: 1.85, HeadingRad: 0, SpeedMs: 2.2, Curvature: 1.6}, {X: 3.05, Y: 1.7, HeadingRad: -math.Pi / 2, SpeedMs: 2.4, Curvature: 1.4}, // Campus (left kink). {X: 3.25, Y: 1.5, HeadingRad: 0, SpeedMs: 3.0, Curvature: 0.7}, // Stavelot (fast right). {X: 3.7, Y: 1.65, HeadingRad: math.Pi / 2 + 0.3, SpeedMs: 3.4, Curvature: 0.5}, // Up to Blanchimont (long right). {X: 4.1, Y: 2.0, HeadingRad: math.Pi / 2 + 0.6, SpeedMs: 4.0, Curvature: 0.3}, // Blanchimont (very fast right kink). {X: 4.25, Y: 2.4, HeadingRad: math.Pi + 0.4, SpeedMs: 4.6, Curvature: 0.3}, // Bus stop chicane (slow left-right). {X: 4.0, Y: 2.7, HeadingRad: -math.Pi / 2 - 0.3, SpeedMs: 2.4, Curvature: 1.4}, {X: 3.7, Y: 2.55, HeadingRad: math.Pi, SpeedMs: 2.4, Curvature: 1.4}, {X: 3.55, Y: 2.75, HeadingRad: math.Pi + 0.7, SpeedMs: 2.4, Curvature: 1.4}, // Back to La Source hairpin. {X: 3.05, Y: 2.7, HeadingRad: -math.Pi / 2, SpeedMs: 2.0, Curvature: 1.8}, {X: 2.85, Y: 2.45, HeadingRad: 0, SpeedMs: 2.0, Curvature: 1.8}, // Pit straight. {X: 2.0, Y: 2.2, HeadingRad: math.Pi - 0.3, SpeedMs: 3.6, Curvature: 0.3}, {X: 1.2, Y: 1.6, HeadingRad: math.Pi - 0.6, SpeedMs: 4.0, Curvature: 0.2}, {X: 0.6, Y: 1.05, HeadingRad: math.Pi - 0.3, SpeedMs: 4.4, Curvature: 0.2}, } b := ComputeBounds(wps) return Track{ ID: "belgium-spa", Name: "Belgium (Spa-Francorchamps)", Description: "Spa-Francorchamps. Long, fast, legendary Eau Rouge / Raidillon.", AuthorID: "system", Visibility: "system", Scale: 27, LengthM: b.LengthM, WidthM: b.WidthM, LaneWidthM: 0.18, Surface: "carpet", Tags: []string{"f1", "long", "fast", "spa"}, Centerline: wps, Bounds: b, BestLapHolder: "Verstappen", } } // --------------------------------------------------------------------------- // Cars — F1 2024 constructors // // Real cars are 1:1 with ~5.7 × 2.0 × 0.95 m, ~798 kg, ~1000 hp. // We model them at 1/24 scale so the largest dimension (~238 mm) still // fits the catalog physical-envelope check. // --------------------------------------------------------------------------- // RedBullRB20 — Oracle Red Bull Racing, Verstappen / Perez. // 2024: 9 wins (last year of dominant era), Constructors' champion. func RedBullRB20() Car { return Car{ ID: "f1-2024-redbull-rb20", Name: "Oracle Red Bull Racing RB20", OwnerID: "system", Visibility: "system", Scale: 24, LengthMm: 238, // 5.7m IRL WidthMm: 83, // 2.0m IRL HeightMm: 40, // 0.95m IRL WeightG: 33, // 798 kg IRL Chassis: Chassis{ Model: "RB20 carbon monocoque", Material: "carbon-fibre", Printed: false, WheelbaseMm: 150, // 3.6m IRL TrackMm: 75, // 1.8m IRL }, Motor: Motor{ Kind: "turbo-hybrid", Class: "F1 PU", KV: 0, PowerW: 746000, // ~1000 hp ICE + MGU-K, peak 746 kW }, Battery: Battery{ VoltageV: 0, CapacityMah: 0, Cells: 0, Chemistry: "li-ion MGU-K", }, Drive: "2WD", TopSpeedMs: 95, // ~340 km/h ColorHex: "#1e3a8a", // dark blue + red/yellow accents Active: true, TotalRaces: 0, TotalLaps: 0, BestLapMs: 0, } } // FerrariSF24 — Scuderia Ferrari, Leclerc / Sainz. func FerrariSF24() Car { return Car{ ID: "f1-2024-ferrari-sf24", Name: "Scuderia Ferrari SF-24", OwnerID: "system", Visibility: "system", Scale: 24, LengthMm: 238, WidthMm: 83, HeightMm: 40, WeightG: 33, Chassis: Chassis{ Model: "SF-24 carbon monocoque", Material: "carbon-fibre", Printed: false, WheelbaseMm: 150, TrackMm: 75, }, Motor: Motor{ Kind: "turbo-hybrid", Class: "F1 PU 066/12", KV: 0, PowerW: 746000, }, Battery: Battery{ Chemistry: "li-ion MGU-K", }, Drive: "2WD", TopSpeedMs: 94, ColorHex: "#dc2626", // rosso corsa Active: true, } } // McLarenMCL38 — McLaren Racing, Norris / Piastri. 2024 Constructors' runner-up. func McLarenMCL38() Car { return Car{ ID: "f1-2024-mclaren-mcl38", Name: "McLaren MCL38", OwnerID: "system", Visibility: "system", Scale: 24, LengthMm: 238, WidthMm: 83, HeightMm: 40, WeightG: 33, Chassis: Chassis{ Model: "MCL38 carbon monocoque", Material: "carbon-fibre", Printed: false, WheelbaseMm: 150, TrackMm: 75, }, Motor: Motor{ Kind: "turbo-hybrid", Class: "F1 PU Mercedes-derived", KV: 0, PowerW: 746000, }, Battery: Battery{ Chemistry: "li-ion MGU-K", }, Drive: "2WD", TopSpeedMs: 94, ColorHex: "#ea580c", // papaya orange Active: true, } } // MercedesW15 — Mercedes-AMG Petronas, Russell / Hamilton (last year of Hamilton at Mercedes). func MercedesW15() Car { return Car{ ID: "f1-2024-mercedes-w15", Name: "Mercedes-AMG W15", OwnerID: "system", Visibility: "system", Scale: 24, LengthMm: 238, WidthMm: 83, HeightMm: 40, WeightG: 33, Chassis: Chassis{ Model: "W15 carbon monocoque", Material: "carbon-fibre", Printed: false, WheelbaseMm: 150, TrackMm: 75, }, Motor: Motor{ Kind: "turbo-hybrid", Class: "F1 PU M15", KV: 0, PowerW: 746000, }, Battery: Battery{ Chemistry: "li-ion MGU-K", }, Drive: "2WD", TopSpeedMs: 94, ColorHex: "#06b6d4", // silver-petronas cyan Active: true, } } // AstonMartinAMR24 — Aston Martin Aramco, Alonso / Stroll. func AstonMartinAMR24() Car { return Car{ ID: "f1-2024-astonmartin-amr24", Name: "Aston Martin Aramco AMR24", OwnerID: "system", Visibility: "system", Scale: 24, LengthMm: 238, WidthMm: 83, HeightMm: 40, WeightG: 33, Chassis: Chassis{ Model: "AMR24 carbon monocoque", Material: "carbon-fibre", Printed: false, WheelbaseMm: 150, TrackMm: 75, }, Motor: Motor{ Kind: "turbo-hybrid", Class: "F1 PU Mercedes-derived", KV: 0, PowerW: 746000, }, Battery: Battery{ Chemistry: "li-ion MGU-K", }, Drive: "2WD", TopSpeedMs: 94, ColorHex: "#16a34a", // racing green Active: true, } } // ComputeBounds derives a bounding box from a closed loop of waypoints. func ComputeBounds(loop []Waypoint) Bounds { if len(loop) == 0 { return Bounds{} } b := Bounds{ MinX: loop[0].X, MinY: loop[0].Y, MaxX: loop[0].X, MaxY: loop[0].Y, } for _, p := range loop[1:] { if p.X < b.MinX { b.MinX = p.X } if p.Y < b.MinY { b.MinY = p.Y } if p.X > b.MaxX { b.MaxX = p.X } if p.Y > b.MaxY { b.MaxY = p.Y } } b.LengthM = b.MaxX - b.MinX b.WidthM = b.MaxY - b.MinY return b }