Browse Source

Add Direction for 4 colored directional Lines

Trains still do not follow lines. Lines do not reach every station. I need to
redo the Line creation.
Tyler Hallada 6 years ago
parent
commit
95805184ea
5 changed files with 146 additions and 21 deletions
  1. 41 0
      src/Direction.ts
  2. 52 10
      src/Line.ts
  3. 17 3
      src/Station.ts
  4. 28 8
      src/transport.ts
  5. 8 0
      src/utils.ts

+ 41 - 0
src/Direction.ts

@@ -0,0 +1,41 @@
1
+import * as PIXI from 'pixi.js';
2
+
3
+import { angleDegrees } from './utils';
4
+
5
+enum Direction {
6
+  North, // 0
7
+  Northeast, // 1
8
+  East, // 2
9
+  Southeast, // 3
10
+  South, // 4
11
+  Southwest, // 5
12
+  West, // 6
13
+  Northwest, // 7
14
+}
15
+
16
+export const getPointDirection = (pointA: PIXI.Point, pointB: PIXI.Point): Direction => {
17
+  const angle = angleDegrees(pointA, pointB);
18
+  let direction = null;
19
+  if (angle >= 337.5 || angle < 22.5) {
20
+    direction = Direction.North;
21
+  } else if (angle >= 22.5 && angle < 67.5) {
22
+    direction = Direction.Northeast;
23
+  } else if (angle >= 67.5 && angle < 112.5) {
24
+    direction = Direction.East;
25
+  } else if (angle >= 112.5 && angle < 157.5) {
26
+    direction = Direction.Southeast;
27
+  } else if (angle >= 157.5 && angle < 202.5) {
28
+    direction = Direction.South;
29
+  } else if (angle >= 202.5 && angle < 247.5) {
30
+    direction = Direction.Southwest;
31
+  } else if (angle >= 247.5 && angle < 292.5) {
32
+    direction = Direction.West;
33
+  } else if (angle >= 292.5 && angle < 337.5) {
34
+    direction = Direction.Northwest;
35
+  } else {
36
+    throw Error('Angle between points is not a valid degree');
37
+  }
38
+  return direction;
39
+};
40
+
41
+export default Direction;

+ 52 - 10
src/Line.ts

@@ -1,25 +1,67 @@
1
+import * as tinycolor from 'tinycolor2';
2
+
3
+import Direction, { getPointDirection } from './Direction';
1 4
 import Station from './Station';
2 5
 import { distance, randomInt, randomPoint } from './utils';
3 6
 
7
+const CONNECTION_RADIUS = Math.floor(Math.sqrt(
8
+  Math.pow(window.innerWidth, 2) + Math.pow(window.innerHeight, 2),
9
+) / 4);
10
+
4 11
 export default class Line {
12
+  public static getLinesWithStation(lines: Line[], station: Station): Line[] {
13
+    return lines.filter(line => line.stations.indexOf(station) >= 0);
14
+  }
15
+
5 16
   public stations: Station[];
17
+  public color: tinycolorInstance;
6 18
 
7
-  constructor(stations: Station[], numStations: number) {
19
+  constructor(stations: Station[], start: PIXI.Point, startDirection: Direction,
20
+              numStations: number, color: tinycolorInstance) {
21
+    this.color = color;
8 22
     this.stations = [];
9
-    let stationsLeft = stations;
10
-    let largest = Station.largestStation(stationsLeft);
11
-    stationsLeft = stationsLeft.filter(s => s !== largest);
12
-    this.stations.push(largest);
23
+    let stationsLeft = stations.slice();
24
+    let currentStation = Station.randomCloseLargeStation(stationsLeft, start, CONNECTION_RADIUS);
25
+    stationsLeft = stationsLeft.filter(s => s !== currentStation);
26
+    this.stations.push(currentStation);
13 27
 
28
+    let direction = startDirection;
14 29
     while (this.stations.length < numStations) {
15
-      largest = Station.largestStation(
16
-        Station.stationsWithinRadius(stationsLeft, largest.location, 500),
30
+      const previousStation = this.stations[this.stations.length - 1];
31
+      let candidateStations = Station.stationsWithinRadius(
32
+        stationsLeft,
33
+        currentStation.location,
34
+        CONNECTION_RADIUS,
17 35
       );
18
-      if (largest === null) {
36
+
37
+      if (this.stations.length > 1) {
38
+        const secondPreviousStation = this.stations[this.stations.length - 2];
39
+        direction = getPointDirection(secondPreviousStation.location,
40
+                                      previousStation.location);
41
+      }
42
+
43
+      const straightStations = Station.stationsInDirection(
44
+        candidateStations, previousStation.location, direction,
45
+      );
46
+      const leftStations = Station.stationsInDirection(
47
+        candidateStations, previousStation.location, (direction - 1) % 7,
48
+      );
49
+      const rightStations = Station.stationsInDirection(
50
+        candidateStations, previousStation.location, (direction + 1) % 7,
51
+      );
52
+      candidateStations = [
53
+        ...straightStations,
54
+        ...leftStations,
55
+        ...rightStations,
56
+      ];
57
+
58
+      currentStation = Station.randomCloseLargeStation(candidateStations, previousStation.location,
59
+                                                       CONNECTION_RADIUS);
60
+      if (currentStation === null || currentStation === undefined) {
19 61
         break;
20 62
       }
21
-      stationsLeft = stationsLeft.filter(s => s !== largest);
22
-      this.stations.push(largest);
63
+      stationsLeft = stationsLeft.filter(s => s !== currentStation);
64
+      this.stations.push(currentStation);
23 65
     }
24 66
   }
25 67
 }

+ 17 - 3
src/Station.ts

@@ -1,6 +1,7 @@
1 1
 import * as tinycolor from 'tinycolor2';
2 2
 
3
-import { distance, randomPoint } from './utils';
3
+import Direction, { getPointDirection } from './Direction';
4
+import { distance, randomPoint, weightedRandom } from './utils';
4 5
 
5 6
 let stationCount = 0;
6 7
 
@@ -21,12 +22,25 @@ export default class Station {
21 22
     return stations.filter(station => distance(point, station.location) <= radius);
22 23
   }
23 24
 
24
-  public static closestStation(stations: Station[], point: PIXI.Point, num: number): Station {
25
+  public static stationsInDirection(stations: Station[], point: PIXI.Point,
26
+                                    direction: Direction): Station[] {
27
+    return stations.filter(station => getPointDirection(point, station.location) === direction);
28
+  }
29
+
30
+  public static closestStation(stations: Station[], point: PIXI.Point): Station {
25 31
     return stations.reduce(
26
-      (prev, curr) => distance(point, prev.location) > distance(point, curr.location) ? prev : curr,
32
+      (prev, curr) => distance(point, prev.location) < distance(point, curr.location) ? prev : curr,
27 33
     );
28 34
   }
29 35
 
36
+  public static randomCloseLargeStation(stations: Station[], point: PIXI.Point,
37
+                                        radius: number): Station {
38
+    const closeStations = Station.stationsWithinRadius(stations, point,
39
+                                                       radius);
40
+    const closeStationWeights = closeStations.map(station => station.population);
41
+    return weightedRandom(closeStations, closeStationWeights);
42
+  }
43
+
30 44
   public static isPointDistant(point: PIXI.Point, stations: Station[],
31 45
                                minDistance: number): boolean {
32 46
     for (const station of stations) {

+ 28 - 8
src/transport.ts

@@ -1,6 +1,7 @@
1 1
 import * as PIXI from 'pixi.js';
2 2
 import * as tinycolor from 'tinycolor2';
3 3
 
4
+import Direction from './Direction';
4 5
 import Line from './Line';
5 6
 import Station from './Station';
6 7
 import Train from './Train';
@@ -119,11 +120,14 @@ const drawTrains = (trains: Train[], graphics: PIXI.Graphics) => {
119 120
   }
120 121
 };
121 122
 
122
-const drawLine = (line: Line, graphics: PIXI.Graphics) => {
123
-  const start = line.stations[0].location;
124
-  graphics.moveTo(start.x, start.y);
125
-  for (const station of line.stations.slice(1)) {
126
-    graphics.lineTo(station.location.x, station.location.y);
123
+const drawLines = (lines: Line[], graphics: PIXI.Graphics) => {
124
+  for (const line of lines) {
125
+    graphics.lineStyle(1, parseInt(line.color.toHex(), 16), 1);
126
+    const start = line.stations[0].location;
127
+    graphics.moveTo(start.x, start.y);
128
+    for (const station of line.stations.slice(1)) {
129
+      graphics.lineTo(station.location.x, station.location.y);
130
+    }
127 131
   }
128 132
 };
129 133
 
@@ -143,7 +147,24 @@ const run = () => {
143 147
 
144 148
   const stations = initStations(30);
145 149
   const trains = initTrains(50, stations);
146
-  // const line = new Line(stations, 10);
150
+  const lines = [
151
+    new Line(
152
+      stations, new PIXI.Point(0, 0),
153
+      Direction.Southeast, 12, tinycolor('red'),
154
+    ),
155
+    new Line(
156
+      stations, new PIXI.Point(window.innerWidth, 0),
157
+      Direction.Southwest, 12, tinycolor('darkcyan'),
158
+    ),
159
+    new Line(
160
+      stations, new PIXI.Point(window.innerWidth, window.innerHeight),
161
+      Direction.Northwest, 12, tinycolor('yellow'),
162
+    ),
163
+    new Line(
164
+      stations, new PIXI.Point(0, window.innerHeight),
165
+      Direction.Northeast, 12, tinycolor('green'),
166
+    ),
167
+  ];
147 168
 
148 169
   ticker.stop();
149 170
   ticker.add((deltaTime) => {
@@ -158,8 +179,7 @@ const run = () => {
158 179
     graphics.lineStyle(1, 0xAEAEAE, 1);
159 180
     drawTrains(trains, graphics);
160 181
 
161
-    // TODO: ?
162
-    // drawLine(line, graphics);
182
+    drawLines(lines, graphics);
163 183
   });
164 184
   ticker.start();
165 185
 

+ 8 - 0
src/utils.ts

@@ -42,3 +42,11 @@ export const rangeMap = (num: number, inMin: number, inMax: number,
42 42
                          outMin: number, outMax: number): number => (
43 43
   (num - inMin) * (outMax - outMin) / (inMax - inMin) + outMin
44 44
 );
45
+
46
+export const angleRadians = (pointA: PIXI.Point, pointB: PIXI.Point): number => (
47
+  Math.atan2(-(pointB.x - pointA.x), pointB.y - pointA.y)
48
+);
49
+
50
+export const angleDegrees = (pointA: PIXI.Point, pointB: PIXI.Point): number => (
51
+  180 + angleRadians(pointA, pointB) * (180 / Math.PI)
52
+);