prishasinghai commited on
Commit
5c3c624
Β·
verified Β·
1 Parent(s): d532f03

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +73 -112
index.html CHANGED
@@ -261,9 +261,7 @@
261
  </header>
262
 
263
  <div class="dashboard">
264
- <!-- ============================================================== -->
265
- <!-- LEFT PANEL: USER SLIDER INPUTS & ONLINE ORDERING -->
266
- <!-- ============================================================== -->
267
  <div class="controls-panel">
268
  <h2>βš™οΈ Grandstand Crowd Inputs</h2>
269
 
@@ -294,7 +292,6 @@
294
 
295
  <hr style="border-color: #333; margin: 15px 0;">
296
 
297
- <!-- ONLINE ORDERING SECTION -->
298
  <h2 style="color: #f39c12;">πŸ” Click & Collect Food Express</h2>
299
  <button class="order-btn" onclick="openOrderModal()">πŸ›’ Place Express Food/Merch Order</button>
300
 
@@ -316,18 +313,14 @@
316
  </div>
317
  </div>
318
 
319
- <!-- ============================================================== -->
320
- <!-- RIGHT PANEL: HTML5 CANVAS MAP & TEXT ANALYSIS REPORT -->
321
- <!-- ============================================================== -->
322
  <div class="map-panel">
323
  <canvas id="silverstoneCanvas" width="900" height="520"></canvas>
324
- <div id="reportBox" class="report-box">
325
- <!-- Dynamic Text Report Injected Here -->
326
- </div>
327
  </div>
328
  </div>
329
 
330
- <!-- ONLINE ORDER MODAL DIALOG -->
331
  <div id="orderModal" class="modal">
332
  <div class="modal-content">
333
  <h3>πŸ” Place Online Pickup Order</h3>
@@ -360,63 +353,47 @@
360
  </div>
361
  </div>
362
 
363
- <!-- ================================================================== -->
364
- <!-- 2. JAVASCRIPT SIMULATION & CANVAS DRAWING LOGIC -->
365
- <!-- ================================================================== -->
366
  <script>
367
- // Gate Capacity Limits
368
  const GATE_1_CAPACITY = 50;
369
  const GATE_3_CAPACITY = 45;
370
 
371
- // Active Orders State Management
372
  let activeOrders = [];
373
  let orderCounter = 101;
374
 
375
  function updateSimulation() {
376
- // 1. READ SLIDER VALUES FROM HTML INPUTS
377
  const hamiltonCrowd = parseInt(document.getElementById('hamilton').value);
378
  const beckettsCrowd = parseInt(document.getElementById('becketts').value);
379
  const stoweCrowd = parseInt(document.getElementById('stowe').value);
380
  const villageCrowd = parseInt(document.getElementById('village').value);
381
  const clubCrowd = parseInt(document.getElementById('club').value);
382
 
383
- // Update UI slider text badges
384
  document.getElementById('hamilton-val').innerText = hamiltonCrowd;
385
  document.getElementById('becketts-val').innerText = beckettsCrowd;
386
  document.getElementById('stowe-val').innerText = stoweCrowd;
387
  document.getElementById('village-val').innerText = villageCrowd;
388
  document.getElementById('club-val').innerText = clubCrowd;
389
 
390
- // 2. MATHEMATICAL TRAFFIC LOAD CALCULATIONS
391
  const gate1Load = Math.round(hamiltonCrowd * 0.5 + beckettsCrowd * 0.3 + villageCrowd * 0.3);
392
  const gate2Load = Math.round(hamiltonCrowd * 0.2 + clubCrowd * 0.4);
393
  const gate3Load = Math.round(stoweCrowd * 0.6 + beckettsCrowd * 0.4);
394
  const gate4Load = Math.round(villageCrowd * 0.4 + hamiltonCrowd * 0.2);
395
 
396
- // Bottleneck flags
397
  const isGate1Bottleneck = gate1Load > GATE_1_CAPACITY;
398
  const isGate3Bottleneck = gate3Load > GATE_3_CAPACITY;
399
  const excessGate1 = isGate1Bottleneck ? (gate1Load - GATE_1_CAPACITY) : 0;
400
  const excessGate3 = isGate3Bottleneck ? (gate3Load - GATE_3_CAPACITY) : 0;
401
 
402
- // 3. DRAW MAP ON HTML5 CANVAS
403
  drawCanvasMap(
404
  hamiltonCrowd, beckettsCrowd, stoweCrowd, villageCrowd, clubCrowd,
405
  gate1Load, gate3Load, isGate1Bottleneck, isGate3Bottleneck
406
  );
407
 
408
- // 4. UPDATE TEXT REPORT
409
  updateReport(gate1Load, gate2Load, gate3Load, gate4Load, isGate1Bottleneck, isGate3Bottleneck, excessGate1, excessGate3);
410
  }
411
 
412
- /* --- ONLINE ORDERING FUNCTIONS --- */
413
- function openOrderModal() {
414
- document.getElementById('orderModal').style.display = 'flex';
415
- }
416
-
417
- function closeOrderModal() {
418
- document.getElementById('orderModal').style.display = 'none';
419
- }
420
 
421
  function submitOrder() {
422
  const location = document.getElementById('pickupLocation').value;
@@ -434,9 +411,8 @@
434
  activeOrders.push(newOrder);
435
  closeOrderModal();
436
  renderOrdersUI();
437
- updateSimulation(); // Refresh Canvas Map Badges
438
 
439
- // Automatically transition order status to Ready after 8 seconds
440
  setTimeout(() => {
441
  newOrder.status = 'Ready for Pickup';
442
  renderOrdersUI();
@@ -459,7 +435,7 @@
459
 
460
  container.innerHTML = activeOrders.map(o => `
461
  <div class="order-card ${o.status === 'Ready for Pickup' ? 'ready' : ''}">
462
- <div style="display:flex; justify-space-between; font-weight:bold;">
463
  <span>${o.id} - ${o.customer}</span>
464
  <span style="color: ${o.status === 'Ready for Pickup' ? '#00cc66' : '#f39c12'}">${o.status}</span>
465
  </div>
@@ -474,11 +450,10 @@
474
  const canvas = document.getElementById('silverstoneCanvas');
475
  const ctx = canvas.getContext('2d');
476
 
477
- // Clear canvas background
478
  ctx.fillStyle = '#181818';
479
  ctx.fillRect(0, 0, canvas.width, canvas.height);
480
 
481
- // Map coordinate converter
482
  function toCanvas(x, y) {
483
  return {
484
  x: x * 65 + 430,
@@ -486,28 +461,23 @@
486
  };
487
  }
488
 
489
- // Helper function to draw multi-point paths
490
- function drawPath(points, color, isDashed = false) {
491
- ctx.beginPath();
492
- if (isDashed) ctx.setLineDash([8, 6]);
493
- points.forEach((ptKey, idx) => {
494
- const pos = typeof ptKey === 'string' ? nodes[ptKey].pos : toCanvas(ptKey.x, ptKey.y);
495
- if (idx === 0) ctx.moveTo(pos.x, pos.y);
496
- else ctx.lineTo(pos.x, pos.y);
497
- });
498
- ctx.strokeStyle = color;
499
- ctx.lineWidth = isDashed ? 5 : 4;
500
- ctx.stroke();
501
- if (isDashed) ctx.setLineDash([]);
502
- }
503
-
504
- // -------------------------------------------------------------
505
- // A. DRAW F1 CIRCUIT TRACK
506
- // -------------------------------------------------------------
507
  const trackPoints = [
508
- {x: -1, y: 0}, {x: 1, y: -2}, {x: 3, y: -3}, {x: 2, y: -4},
509
- {x: -1, y: -3}, {x: -2, y: -1}, {x: -3, y: 1}, {x: -1, y: 3},
510
- {x: 1, y: 4}, {x: 3, y: 4}, {x: 4, y: 2}, {x: 2, y: 0}, {x: -1, y: 0}
 
 
 
 
 
 
 
 
 
 
 
 
511
  ];
512
 
513
  // Outer track border
@@ -532,97 +502,92 @@
532
  ctx.strokeStyle = '#aaaaaa';
533
  ctx.lineWidth = 1.5;
534
  ctx.stroke();
535
- ctx.setLineDash([]); // Reset dash
536
 
537
- // -------------------------------------------------------------
538
- // B. DEFINE MAP NODES (Grandstands, Food, Hubs, Gates)
539
- // Shifted safely outside the track boundary
540
- // -------------------------------------------------------------
541
  const nodes = {
542
  // Grandstands
543
- "Hamilton GS": { pos: toCanvas(-2.2, -2.5), type: "gs", label: `Hamilton (${hamilton})` },
544
- "Becketts GS": { pos: toCanvas(1, 4.8), type: "gs", label: `Becketts (${becketts})` },
545
- "Stowe GS": { pos: toCanvas(4.8, 1.5), type: "gs", label: `Stowe (${stowe})` },
546
- "Village GS": { pos: toCanvas(-4.2, 1.2), type: "gs", label: `Village (${village})` },
547
- "Club GS": { pos: toCanvas(2.8, -4.5), type: "gs", label: `Club (${club})` },
548
 
549
  // Food Stalls & Arenas
550
- "Main Fan Zone": { pos: toCanvas(-4.2, -1.0), type: "food", label: "πŸ” Main Fan Zone" },
551
- "Becketts Food": { pos: toCanvas(2.8, 4.2), type: "food", label: "🌭 Becketts Food" },
552
- "Village Plaza": { pos: toCanvas(-3.2, 3.2), type: "food", label: "πŸ• Village Plaza" },
553
- "Club Eats": { pos: toCanvas(-0.5, -4.5), type: "food", label: "🍦 Club Eats" },
554
 
555
  // Intermediate Junction Hubs
556
- "Arena Crossroad": { pos: toCanvas(-3.5, -2.2), type: "hub", label: "πŸ“ Arena Hub" },
557
- "Becketts Interchange": { pos: toCanvas(-1.0, 4.2), type: "hub", label: "πŸ“ Becketts Hub" },
558
- "South Bypass": { pos: toCanvas(-2.2, -3.8), type: "hub", label: "πŸ“ South Hub" },
559
-
560
- // Gates
561
- "Main Gate 1": { pos: toCanvas(-4.8, -2.2), type: "gate", label: "πŸšͺ Main Gate 1" },
562
- "South Gate 2": { pos: toCanvas(1.5, -4.8), type: "gate", label: "πŸšͺ South Gate 2" },
563
- "East Gate 3": { pos: toCanvas(5.2, -1.5), type: "gate", label: "πŸšͺ East Gate 3" },
564
- "West Gate 4": { pos: toCanvas(-4.8, 3.2), type: "gate", label: "πŸšͺ West Gate 4" }
565
  };
566
 
567
- // -------------------------------------------------------------
568
- // C. INTERCONNECTED STANDARD PEDESTRIAN PATHS (GREEN / RED)
569
- // Explicit waypoint routing around track perimeter
570
- // -------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
571
  const pathColorG1 = isGate1Bottleneck ? "#ff4444" : "#00cc66";
572
  const pathColorG3 = isGate3Bottleneck ? "#ff4444" : "#00cc66";
573
 
574
- // Gate 1 Network
575
  drawPath(["Hamilton GS", "Arena Crossroad"], pathColorG1);
576
  drawPath(["Arena Crossroad", "Main Fan Zone"], pathColorG1);
577
  drawPath(["Main Fan Zone", "Main Gate 1"], pathColorG1);
578
- drawPath(["Village GS", "Main Fan Zone"], pathColorG1);
579
 
580
- // Gate 3 & Becketts Network
581
  drawPath(["Becketts GS", "Becketts Interchange"], pathColorG3);
582
  drawPath(["Becketts GS", "Becketts Food"], pathColorG3);
583
  drawPath(["Becketts Food", "Stowe GS"], pathColorG3);
584
  drawPath(["Stowe GS", "East Gate 3"], pathColorG3);
585
-
586
- // Outer Northern Connection (Becketts Hub <-> Village Plaza <-> Fan Zone around top/left)
587
- drawPath(["Becketts Interchange", "Village Plaza"], pathColorG1);
588
- drawPath(["Village Plaza", "Village GS"], "#00cc66");
589
  drawPath(["Village Plaza", "West Gate 4"], "#00cc66");
590
 
591
- // Outer Southern Connection (Hamilton <-> South Bypass <-> Club Eats <-> South Gate 2 around bottom)
592
  drawPath(["Hamilton GS", "South Bypass"], "#00cc66");
593
  drawPath(["South Bypass", "Club Eats"], "#00cc66");
594
  drawPath(["Club GS", "Club Eats"], "#00cc66");
595
  drawPath(["Club Eats", "South Gate 2"], "#00cc66");
596
 
597
- // -------------------------------------------------------------
598
- // D. EXPANDED GOOGLE MAPS MULTI-NODE DETOURS (PURPLE)
599
- // Detours routed cleanly along the outer perimeter
600
- // -------------------------------------------------------------
601
  if (isGate1Bottleneck) {
602
- // Fan Zone -> South Hub -> Club Eats -> South Gate 2
603
  drawPath(["Main Fan Zone", "Arena Crossroad", "South Bypass", "Club Eats", "South Gate 2"], "#a020f0", true);
604
-
605
- // Arena Hub -> West Gate 4
606
- drawPath(["Arena Crossroad", "Village GS", "West Gate 4"], "#a020f0", true);
607
  }
608
 
609
  if (isGate3Bottleneck) {
610
- // Becketts Hub -> Village Plaza -> Main Fan Zone -> South Hub -> South Gate 2
611
- drawPath(["Becketts Interchange", "Village Plaza", "Main Fan Zone", "South Bypass", "South Gate 2"], "#a020f0", true);
612
  }
613
 
614
- // -------------------------------------------------------------
615
- // E. DRAW NODE BOXES, LABELS & ORDER BADGES
616
- // -------------------------------------------------------------
617
  for (let key in nodes) {
618
  const n = nodes[key];
619
  ctx.font = "bold 11px Arial";
620
  const textWidth = ctx.measureText(n.label).width;
621
  const boxPadding = 7;
622
 
623
- let boxColor = "#3498db"; // Blue for Grandstands
624
- if (n.type === "food") boxColor = "#f39c12"; // Orange for Food
625
- if (n.type === "hub") boxColor = "#7f8c8d"; // Grey for Hubs
626
  if (n.type === "gate") {
627
  if ((key === "Main Gate 1" && isGate1Bottleneck) || (key === "East Gate 3" && isGate3Bottleneck)) {
628
  boxColor = "#ff4444";
@@ -631,7 +596,6 @@
631
  }
632
  }
633
 
634
- // Draw background rounded box
635
  ctx.fillStyle = boxColor;
636
  ctx.beginPath();
637
  ctx.roundRect(
@@ -646,13 +610,11 @@
646
  ctx.lineWidth = 1;
647
  ctx.stroke();
648
 
649
- // Draw label text
650
  ctx.fillStyle = "#ffffff";
651
  ctx.textAlign = "center";
652
  ctx.textBaseline = "middle";
653
  ctx.fillText(n.label, n.pos.x, n.pos.y);
654
 
655
- // DRAW PENDING ORDERS OVERLAY BADGE FOR FOOD STALLS
656
  if (n.type === "food") {
657
  const pendingCount = activeOrders.filter(o => o.location === key).length;
658
  if (pendingCount > 0) {
@@ -671,7 +633,7 @@
671
  }
672
  }
673
 
674
- // Draw Legend
675
  ctx.font = "11px Arial";
676
  ctx.fillStyle = "#ffffff";
677
  ctx.textAlign = "left";
@@ -714,7 +676,6 @@
714
  }
715
  }
716
 
717
- // Initialize simulation on initial page load
718
  window.onload = updateSimulation;
719
  </script>
720
  </body>
 
261
  </header>
262
 
263
  <div class="dashboard">
264
+ <!-- LEFT PANEL -->
 
 
265
  <div class="controls-panel">
266
  <h2>βš™οΈ Grandstand Crowd Inputs</h2>
267
 
 
292
 
293
  <hr style="border-color: #333; margin: 15px 0;">
294
 
 
295
  <h2 style="color: #f39c12;">πŸ” Click & Collect Food Express</h2>
296
  <button class="order-btn" onclick="openOrderModal()">πŸ›’ Place Express Food/Merch Order</button>
297
 
 
313
  </div>
314
  </div>
315
 
316
+ <!-- RIGHT PANEL -->
 
 
317
  <div class="map-panel">
318
  <canvas id="silverstoneCanvas" width="900" height="520"></canvas>
319
+ <div id="reportBox" class="report-box"></div>
 
 
320
  </div>
321
  </div>
322
 
323
+ <!-- MODAL -->
324
  <div id="orderModal" class="modal">
325
  <div class="modal-content">
326
  <h3>πŸ” Place Online Pickup Order</h3>
 
353
  </div>
354
  </div>
355
 
356
+ <!-- SCRIPT -->
 
 
357
  <script>
 
358
  const GATE_1_CAPACITY = 50;
359
  const GATE_3_CAPACITY = 45;
360
 
 
361
  let activeOrders = [];
362
  let orderCounter = 101;
363
 
364
  function updateSimulation() {
 
365
  const hamiltonCrowd = parseInt(document.getElementById('hamilton').value);
366
  const beckettsCrowd = parseInt(document.getElementById('becketts').value);
367
  const stoweCrowd = parseInt(document.getElementById('stowe').value);
368
  const villageCrowd = parseInt(document.getElementById('village').value);
369
  const clubCrowd = parseInt(document.getElementById('club').value);
370
 
 
371
  document.getElementById('hamilton-val').innerText = hamiltonCrowd;
372
  document.getElementById('becketts-val').innerText = beckettsCrowd;
373
  document.getElementById('stowe-val').innerText = stoweCrowd;
374
  document.getElementById('village-val').innerText = villageCrowd;
375
  document.getElementById('club-val').innerText = clubCrowd;
376
 
 
377
  const gate1Load = Math.round(hamiltonCrowd * 0.5 + beckettsCrowd * 0.3 + villageCrowd * 0.3);
378
  const gate2Load = Math.round(hamiltonCrowd * 0.2 + clubCrowd * 0.4);
379
  const gate3Load = Math.round(stoweCrowd * 0.6 + beckettsCrowd * 0.4);
380
  const gate4Load = Math.round(villageCrowd * 0.4 + hamiltonCrowd * 0.2);
381
 
 
382
  const isGate1Bottleneck = gate1Load > GATE_1_CAPACITY;
383
  const isGate3Bottleneck = gate3Load > GATE_3_CAPACITY;
384
  const excessGate1 = isGate1Bottleneck ? (gate1Load - GATE_1_CAPACITY) : 0;
385
  const excessGate3 = isGate3Bottleneck ? (gate3Load - GATE_3_CAPACITY) : 0;
386
 
 
387
  drawCanvasMap(
388
  hamiltonCrowd, beckettsCrowd, stoweCrowd, villageCrowd, clubCrowd,
389
  gate1Load, gate3Load, isGate1Bottleneck, isGate3Bottleneck
390
  );
391
 
 
392
  updateReport(gate1Load, gate2Load, gate3Load, gate4Load, isGate1Bottleneck, isGate3Bottleneck, excessGate1, excessGate3);
393
  }
394
 
395
+ function openOrderModal() { document.getElementById('orderModal').style.display = 'flex'; }
396
+ function closeOrderModal() { document.getElementById('orderModal').style.display = 'none'; }
 
 
 
 
 
 
397
 
398
  function submitOrder() {
399
  const location = document.getElementById('pickupLocation').value;
 
411
  activeOrders.push(newOrder);
412
  closeOrderModal();
413
  renderOrdersUI();
414
+ updateSimulation();
415
 
 
416
  setTimeout(() => {
417
  newOrder.status = 'Ready for Pickup';
418
  renderOrdersUI();
 
435
 
436
  container.innerHTML = activeOrders.map(o => `
437
  <div class="order-card ${o.status === 'Ready for Pickup' ? 'ready' : ''}">
438
+ <div style="display:flex; justify-content:space-between; font-weight:bold;">
439
  <span>${o.id} - ${o.customer}</span>
440
  <span style="color: ${o.status === 'Ready for Pickup' ? '#00cc66' : '#f39c12'}">${o.status}</span>
441
  </div>
 
450
  const canvas = document.getElementById('silverstoneCanvas');
451
  const ctx = canvas.getContext('2d');
452
 
 
453
  ctx.fillStyle = '#181818';
454
  ctx.fillRect(0, 0, canvas.width, canvas.height);
455
 
456
+ // Coordinate system mapping
457
  function toCanvas(x, y) {
458
  return {
459
  x: x * 65 + 430,
 
461
  };
462
  }
463
 
464
+ // Real Silverstone circuit geometry outline
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
465
  const trackPoints = [
466
+ {x: -1.5, y: -2.5}, // Hamilton Straight / Grid
467
+ {x: -0.2, y: -2.5}, // Abbey
468
+ {x: 0.5, y: -1.8}, // Farm Curve
469
+ {x: -0.2, y: -1.0}, // Village Loop
470
+ {x: -1.2, y: -1.2}, // The Loop
471
+ {x: -2.2, y: 0.2}, // Wellington Straight
472
+ {x: -2.0, y: 1.8}, // Brooklands / Luffield
473
+ {x: -0.8, y: 2.2}, // Woodcote
474
+ {x: 1.2, y: 3.2}, // Copse
475
+ {x: 3.2, y: 2.8}, // Maggotts / Becketts
476
+ {x: 3.8, y: 1.2}, // Chapel / Hangar Straight
477
+ {x: 3.5, y: -1.2}, // Stowe
478
+ {x: 2.2, y: -3.0}, // Vale
479
+ {x: 0.2, y: -3.2}, // Club
480
+ {x: -1.5, y: -2.5} // Finish Line / Main Straight
481
  ];
482
 
483
  // Outer track border
 
502
  ctx.strokeStyle = '#aaaaaa';
503
  ctx.lineWidth = 1.5;
504
  ctx.stroke();
505
+ ctx.setLineDash([]);
506
 
507
+ // MAP NODES positioned outside the track outline
 
 
 
508
  const nodes = {
509
  // Grandstands
510
+ "Hamilton GS": { pos: toCanvas(-1.0, -3.4), type: "gs", label: `Hamilton (${hamilton})` },
511
+ "Becketts GS": { pos: toCanvas(3.8, 3.5), type: "gs", label: `Becketts (${becketts})` },
512
+ "Stowe GS": { pos: toCanvas(4.8, -1.2), type: "gs", label: `Stowe (${stowe})` },
513
+ "Village GS": { pos: toCanvas(-0.2, -0.2), type: "gs", label: `Village (${village})` },
514
+ "Club GS": { pos: toCanvas(0.5, -4.2), type: "gs", label: `Club (${club})` },
515
 
516
  // Food Stalls & Arenas
517
+ "Main Fan Zone": { pos: toCanvas(-2.8, -1.8), type: "food", label: "πŸ” Main Fan Zone" },
518
+ "Becketts Food": { pos: toCanvas(2.2, 4.0), type: "food", label: "🌭 Becketts Food" },
519
+ "Village Plaza": { pos: toCanvas(-3.2, 1.2), type: "food", label: "πŸ• Village Plaza" },
520
+ "Club Eats": { pos: toCanvas(-1.8, -4.0), type: "food", label: "🍦 Club Eats" },
521
 
522
  // Intermediate Junction Hubs
523
+ "Arena Crossroad": { pos: toCanvas(-3.8, -2.8), type: "hub", label: "πŸ“ Arena Hub" },
524
+ "Becketts Interchange": { pos: toCanvas(0.2, 3.8), type: "hub", label: "πŸ“ Becketts Hub" },
525
+ "South Bypass": { pos: toCanvas(-3.2, -3.8), type: "hub", label: "πŸ“ South Hub" },
526
+
527
+ // Outer Gates
528
+ "Main Gate 1": { pos: toCanvas(-4.8, -2.8), type: "gate", label: "πŸšͺ Main Gate 1" },
529
+ "South Gate 2": { pos: toCanvas(2.5, -4.5), type: "gate", label: "πŸšͺ South Gate 2" },
530
+ "East Gate 3": { pos: toCanvas(5.2, 0.2), type: "gate", label: "πŸšͺ East Gate 3" },
531
+ "West Gate 4": { pos: toCanvas(-4.5, 2.2), type: "gate", label: "πŸšͺ West Gate 4" }
532
  };
533
 
534
+ // Custom Waypoint path drawing function
535
+ function drawPath(points, color, isDashed = false) {
536
+ ctx.beginPath();
537
+ if (isDashed) ctx.setLineDash([8, 6]);
538
+ points.forEach((pt, idx) => {
539
+ const pos = typeof pt === 'string' ? nodes[pt].pos : toCanvas(pt.x, pt.y);
540
+ if (idx === 0) ctx.moveTo(pos.x, pos.y);
541
+ else ctx.lineTo(pos.x, pos.y);
542
+ });
543
+ ctx.strokeStyle = color;
544
+ ctx.lineWidth = isDashed ? 5 : 4;
545
+ ctx.stroke();
546
+ if (isDashed) ctx.setLineDash([]);
547
+ }
548
+
549
  const pathColorG1 = isGate1Bottleneck ? "#ff4444" : "#00cc66";
550
  const pathColorG3 = isGate3Bottleneck ? "#ff4444" : "#00cc66";
551
 
552
+ // Standard Pathways routed around the track perimeter
553
  drawPath(["Hamilton GS", "Arena Crossroad"], pathColorG1);
554
  drawPath(["Arena Crossroad", "Main Fan Zone"], pathColorG1);
555
  drawPath(["Main Fan Zone", "Main Gate 1"], pathColorG1);
556
+ drawPath(["Village GS", {x: -2.8, y: 0.2}, "Village Plaza"], pathColorG1);
557
 
 
558
  drawPath(["Becketts GS", "Becketts Interchange"], pathColorG3);
559
  drawPath(["Becketts GS", "Becketts Food"], pathColorG3);
560
  drawPath(["Becketts Food", "Stowe GS"], pathColorG3);
561
  drawPath(["Stowe GS", "East Gate 3"], pathColorG3);
562
+
563
+ drawPath(["Becketts Interchange", {x: -2.5, y: 3.5}, "Village Plaza"], pathColorG1);
 
 
564
  drawPath(["Village Plaza", "West Gate 4"], "#00cc66");
565
 
 
566
  drawPath(["Hamilton GS", "South Bypass"], "#00cc66");
567
  drawPath(["South Bypass", "Club Eats"], "#00cc66");
568
  drawPath(["Club GS", "Club Eats"], "#00cc66");
569
  drawPath(["Club Eats", "South Gate 2"], "#00cc66");
570
 
571
+ // Google Maps Detour Routing (Purple Lines) around perimeter
 
 
 
572
  if (isGate1Bottleneck) {
 
573
  drawPath(["Main Fan Zone", "Arena Crossroad", "South Bypass", "Club Eats", "South Gate 2"], "#a020f0", true);
574
+ drawPath(["Arena Crossroad", {x: -4.8, y: -0.2}, "West Gate 4"], "#a020f0", true);
 
 
575
  }
576
 
577
  if (isGate3Bottleneck) {
578
+ drawPath(["Becketts Interchange", {x: -2.5, y: 3.5}, "Village Plaza", "Main Fan Zone", "South Bypass", "South Gate 2"], "#a020f0", true);
 
579
  }
580
 
581
+ // Draw Nodes, Badges & Labels
 
 
582
  for (let key in nodes) {
583
  const n = nodes[key];
584
  ctx.font = "bold 11px Arial";
585
  const textWidth = ctx.measureText(n.label).width;
586
  const boxPadding = 7;
587
 
588
+ let boxColor = "#3498db";
589
+ if (n.type === "food") boxColor = "#f39c12";
590
+ if (n.type === "hub") boxColor = "#7f8c8d";
591
  if (n.type === "gate") {
592
  if ((key === "Main Gate 1" && isGate1Bottleneck) || (key === "East Gate 3" && isGate3Bottleneck)) {
593
  boxColor = "#ff4444";
 
596
  }
597
  }
598
 
 
599
  ctx.fillStyle = boxColor;
600
  ctx.beginPath();
601
  ctx.roundRect(
 
610
  ctx.lineWidth = 1;
611
  ctx.stroke();
612
 
 
613
  ctx.fillStyle = "#ffffff";
614
  ctx.textAlign = "center";
615
  ctx.textBaseline = "middle";
616
  ctx.fillText(n.label, n.pos.x, n.pos.y);
617
 
 
618
  if (n.type === "food") {
619
  const pendingCount = activeOrders.filter(o => o.location === key).length;
620
  if (pendingCount > 0) {
 
633
  }
634
  }
635
 
636
+ // Legend
637
  ctx.font = "11px Arial";
638
  ctx.fillStyle = "#ffffff";
639
  ctx.textAlign = "left";
 
676
  }
677
  }
678
 
 
679
  window.onload = updateSimulation;
680
  </script>
681
  </body>