-
Notifications
You must be signed in to change notification settings - Fork 1
/
BasicVehicle.ts
153 lines (123 loc) · 3.29 KB
/
BasicVehicle.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import { IVehicle, VEHICLE_LENGTH, VEHICLE_WIDTH } from './IVehicle';
import { Waypoint } from './Waypoint';
import { getDistance } from './trig';
import { Point, Circle } from './types';
import cloneDeep from 'lodash/cloneDeep';
const FRICTION = 1 / 20;
class BasicVehicle implements IVehicle {
x: number;
y: number;
prevX = 0;
prevY = 0;
accelValue: number;
speed = 0;
velocityAngle = 0;
color = 'white';
targetWaypoint = 0;
boundingCircle: Circle = { x: 0, y: 0, radius: 0 };
waypoints: Waypoint[] = [];
calcedTurnDecisionDots = [];
constructor(x: number, y: number, accelValue: number, color: string) {
this.x = x;
this.y = y;
this.accelValue = accelValue;
this.color = color;
}
getAngleToWaypoint(): number {
const waypoint = this.waypoints[this.targetWaypoint];
const x = waypoint.x - this.x;
const y = waypoint.y - this.y;
const angle = Math.atan(Math.abs(y) / Math.abs(x));
if (x >= 0 && y >= 0) {
// upper right quadrant
return angle;
}
if (x < 0 && y >= 0) {
// upper left quadrant
return Math.PI - angle;
}
if (x < 0 && y < 0) {
// lower left quadrant
return Math.PI + angle;
}
// lower right quadrant
return 2 * Math.PI - angle;
}
processWaypoint() {
this.velocityAngle = this.getAngleToWaypoint();
}
updateCurrentWaypoint() {
const currentWaypoint = this.waypoints[this.targetWaypoint];
const distance = getDistance(
this.x,
this.y,
currentWaypoint.x,
currentWaypoint.y
);
if (distance <= this.speed) {
this.targetWaypoint += 1;
if (this.targetWaypoint >= this.waypoints.length) {
this.targetWaypoint = 0;
}
}
}
getAirDrag(speed: number) {
return (1 / 12) * speed;
}
handleAcceleration() {
const airDrag = this.getAirDrag(this.speed);
const acceleration = this.accelValue - FRICTION - airDrag;
this.speed = Math.max(0, this.speed + acceleration);
const cos = Math.cos(this.velocityAngle);
const sin = Math.sin(this.velocityAngle);
const velX = this.speed * cos;
const velY = this.speed * sin;
this.x += velX;
this.y += velY;
}
update(waypoints: Waypoint[]) {
this.waypoints = waypoints;
this.updateCurrentWaypoint();
this.processWaypoint();
this.handleAcceleration();
}
steerAwayFrom(_p: Point) {
// not needed, but must implement IVehicle
}
draw(context: CanvasRenderingContext2D, shouldDrawBoundingCircle: boolean) {
context.save();
context.translate(this.x, this.y);
context.rotate(this.velocityAngle);
context.fillStyle = this.color;
context.fillRect(
-VEHICLE_LENGTH / 2,
-VEHICLE_WIDTH / 2,
VEHICLE_LENGTH,
VEHICLE_WIDTH
);
context.restore();
context.save();
context.fillStyle = 'green';
context.fillRect(this.x, this.y, 1, 1);
context.restore();
if (shouldDrawBoundingCircle) {
context.save();
context.translate(this.x, this.y);
context.strokeStyle = 'white';
context.beginPath();
context.arc(0, 0, this.boundingCircle.radius, 0, 2 * Math.PI);
context.stroke();
context.restore();
}
}
clone(): IVehicle {
const clone = new BasicVehicle(this.x, this.y, this.accelValue, this.color);
(Object.keys(this) as Array<keyof BasicVehicle>).forEach((key) => {
if (typeof this[key] !== 'function') {
(clone[key] as any) = cloneDeep(this[key]) as any;
}
});
return clone;
}
}
export { BasicVehicle };