@inventure71/paddockjs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +86 -0
- package/CHANGELOG.md +5 -0
- package/LICENSE +176 -0
- package/README.md +165 -0
- package/assets/f1-broadcast-panel-surface.png +0 -0
- package/assets/f1-car-sprite-game.png +0 -0
- package/assets/f1-driver-helmet.png +0 -0
- package/assets/f1-safety-car-sprite.png +0 -0
- package/assets/f1-texture-asphalt.png +0 -0
- package/assets/f1Logo.png +0 -0
- package/docs/architecture.md +209 -0
- package/docs/concepts.md +178 -0
- package/docs/data_contract.md +360 -0
- package/docs/data_model.md +167 -0
- package/docs/index.md +15 -0
- package/docs/learnings.md +47 -0
- package/docs/rules.md +181 -0
- package/docs/system_specs.md +211 -0
- package/package.json +85 -0
- package/src/api/PaddockSimulatorController.js +233 -0
- package/src/app/F1SimulatorApp.js +1293 -0
- package/src/app/domBindings.js +64 -0
- package/src/config/defaultAssets.js +29 -0
- package/src/config/defaultOptions.js +184 -0
- package/src/data/championship.js +225 -0
- package/src/data/customFields.js +14 -0
- package/src/data/demoDrivers.js +134 -0
- package/src/data/driverData.js +71 -0
- package/src/data/normalizeDrivers.js +21 -0
- package/src/data/vehicleData.js +81 -0
- package/src/index.d.ts +399 -0
- package/src/index.js +116 -0
- package/src/rendering/proceduralTrackAsset.js +517 -0
- package/src/rendering/renderSnapshot.js +23 -0
- package/src/simulation/driverController.js +253 -0
- package/src/simulation/raceSimulation.js +933 -0
- package/src/simulation/simMath.js +34 -0
- package/src/simulation/trackModel.js +607 -0
- package/src/simulation/units.js +30 -0
- package/src/simulation/vehiclePhysics.js +104 -0
- package/src/standalone.js +15 -0
- package/src/styles.css +1818 -0
- package/src/ui/componentTemplates.js +229 -0
- package/src/ui/shellTemplate.js +43 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { clamp, normalizeAngle } from './simMath.js';
|
|
2
|
+
|
|
3
|
+
const G = 9.80665;
|
|
4
|
+
|
|
5
|
+
export const VEHICLE_LIMITS = {
|
|
6
|
+
wheelbase: 3.65,
|
|
7
|
+
maxSteer: 0.56,
|
|
8
|
+
steerRate: 2.35,
|
|
9
|
+
maxSpeed: 192,
|
|
10
|
+
carLength: 66,
|
|
11
|
+
carWidth: 21,
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const SURFACE_MODEL = {
|
|
15
|
+
track: { grip: 1, drag: 0, rollingResistance: 0 },
|
|
16
|
+
kerb: { grip: 0.92, drag: 0.12, rollingResistance: 0.045 },
|
|
17
|
+
gravel: { grip: 0.43, drag: 2.6, rollingResistance: 0.48 },
|
|
18
|
+
grass: { grip: 0.34, drag: 1.9, rollingResistance: 0.36 },
|
|
19
|
+
barrier: { grip: 0.18, drag: 9, rollingResistance: 1.2 },
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
function accelerationLimit(speed, surfaceGrip) {
|
|
23
|
+
const speedRatio = clamp(speed / VEHICLE_LIMITS.maxSpeed, 0, 1);
|
|
24
|
+
return surfaceGrip * (17 * (1 - speedRatio ** 2.1) + 1.2);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function brakingLimit(speed, surfaceGrip) {
|
|
28
|
+
const speedRatio = clamp(speed / VEHICLE_LIMITS.maxSpeed, 0, 1);
|
|
29
|
+
return surfaceGrip * (20 + speedRatio * 14);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function integrateVehiclePhysics(car, controls, dt) {
|
|
33
|
+
const steeringTarget = clamp(controls.steering ?? 0, -VEHICLE_LIMITS.maxSteer, VEHICLE_LIMITS.maxSteer);
|
|
34
|
+
const steerDelta = clamp(
|
|
35
|
+
steeringTarget - car.steeringAngle,
|
|
36
|
+
-VEHICLE_LIMITS.steerRate * dt,
|
|
37
|
+
VEHICLE_LIMITS.steerRate * dt,
|
|
38
|
+
);
|
|
39
|
+
car.steeringAngle += steerDelta;
|
|
40
|
+
|
|
41
|
+
const throttle = clamp(controls.throttle ?? 0, 0, 1);
|
|
42
|
+
const brake = clamp(controls.brake ?? 0, 0, 1);
|
|
43
|
+
const surface = SURFACE_MODEL[car.trackState?.surface] ?? SURFACE_MODEL.track;
|
|
44
|
+
const surfaceGrip = surface.grip;
|
|
45
|
+
const dragMultiplier = car.drsActive ? 0.42 : 1;
|
|
46
|
+
const speedRatio = clamp(car.speed / VEHICLE_LIMITS.maxSpeed, 0, 1);
|
|
47
|
+
const engineForce = throttle * car.powerNewtons * Math.max(0.12, 1 - speedRatio ** 1.15);
|
|
48
|
+
const brakeForce = brake * car.brakeNewtons;
|
|
49
|
+
const dragForce = (car.dragCoefficient * dragMultiplier * 0.12 + surface.drag) * car.speed * car.speed;
|
|
50
|
+
const rollingForce = surface.rollingResistance * car.mass * G;
|
|
51
|
+
const driveAcceleration = clamp(engineForce / car.mass, 0, accelerationLimit(car.speed, surfaceGrip));
|
|
52
|
+
const brakeDeceleration = clamp(brakeForce / car.mass, 0, brakingLimit(car.speed, surfaceGrip));
|
|
53
|
+
const dragDeceleration = (dragForce + rollingForce) / car.mass;
|
|
54
|
+
const acceleration = driveAcceleration - brakeDeceleration - dragDeceleration;
|
|
55
|
+
|
|
56
|
+
car.speed = clamp(car.speed + acceleration * dt, 0, VEHICLE_LIMITS.maxSpeed);
|
|
57
|
+
|
|
58
|
+
const rawYawRate = car.speed / VEHICLE_LIMITS.wheelbase * Math.tan(car.steeringAngle);
|
|
59
|
+
const downforceGrip = car.downforceCoefficient * car.speed * car.speed / car.mass;
|
|
60
|
+
const tyreConditionGrip = clamp(0.82 + (car.tireEnergy ?? 100) / 560, 0.82, 1);
|
|
61
|
+
const maxYawRate = ((car.tireGrip * tyreConditionGrip * G + downforceGrip) * surfaceGrip) / Math.max(car.speed, 8);
|
|
62
|
+
car.yawRate = clamp(rawYawRate, -maxYawRate, maxYawRate);
|
|
63
|
+
car.turnRadius = Math.abs(car.yawRate) < 0.001 ? Infinity : car.speed / Math.abs(car.yawRate);
|
|
64
|
+
car.heading = normalizeAngle(car.heading + car.yawRate * dt);
|
|
65
|
+
car.x += Math.cos(car.heading) * car.speed * dt;
|
|
66
|
+
car.y += Math.sin(car.heading) * car.speed * dt;
|
|
67
|
+
car.throttle = throttle;
|
|
68
|
+
car.brake = brake;
|
|
69
|
+
car.lateralAcceleration = car.speed * car.yawRate;
|
|
70
|
+
car.steerSaturation = rawYawRate === 0 ? 0 : Math.abs(car.yawRate / rawYawRate);
|
|
71
|
+
const tyreLoad = Math.abs(car.lateralAcceleration) / G;
|
|
72
|
+
const wearRate = 0.035 + tyreLoad * 0.11 + brake * 0.07 + throttle * 0.025;
|
|
73
|
+
car.tireEnergy = clamp((car.tireEnergy ?? 100) - wearRate * dt, 38, 100);
|
|
74
|
+
|
|
75
|
+
return car;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function getCarCorners(car) {
|
|
79
|
+
const halfLength = VEHICLE_LIMITS.carLength / 2;
|
|
80
|
+
const halfWidth = VEHICLE_LIMITS.carWidth / 2;
|
|
81
|
+
const cos = Math.cos(car.heading);
|
|
82
|
+
const sin = Math.sin(car.heading);
|
|
83
|
+
const forward = { x: cos, y: sin };
|
|
84
|
+
const right = { x: -sin, y: cos };
|
|
85
|
+
|
|
86
|
+
return [
|
|
87
|
+
{
|
|
88
|
+
x: car.x + forward.x * halfLength + right.x * halfWidth,
|
|
89
|
+
y: car.y + forward.y * halfLength + right.y * halfWidth,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
x: car.x + forward.x * halfLength - right.x * halfWidth,
|
|
93
|
+
y: car.y + forward.y * halfLength - right.y * halfWidth,
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
x: car.x - forward.x * halfLength - right.x * halfWidth,
|
|
97
|
+
y: car.y - forward.y * halfLength - right.y * halfWidth,
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
x: car.x - forward.x * halfLength + right.x * halfWidth,
|
|
101
|
+
y: car.y - forward.y * halfLength + right.y * halfWidth,
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { DEMO_PROJECT_DRIVERS, mountF1Simulator } from './index.js';
|
|
2
|
+
|
|
3
|
+
const root = document.getElementById('f1-simulator-root');
|
|
4
|
+
|
|
5
|
+
if (root) {
|
|
6
|
+
mountF1Simulator(root, {
|
|
7
|
+
drivers: DEMO_PROJECT_DRIVERS,
|
|
8
|
+
onDriverOpen(driver) {
|
|
9
|
+
if (driver.link) window.location.href = driver.link;
|
|
10
|
+
},
|
|
11
|
+
}).catch((error) => {
|
|
12
|
+
root.dataset.simulatorState = 'error';
|
|
13
|
+
console.error('F1 simulator failed to initialize.', error);
|
|
14
|
+
});
|
|
15
|
+
}
|