Browse Source

Rework Line gen, add stats & viewport

Generate 4 separate lines. Trains now follow lines and only spawn on connected
stations.
Tyler Hallada 6 years ago
parent
commit
df9ba6d5ea
7 changed files with 217 additions and 103 deletions
  1. 81 0
      package-lock.json
  2. 3 0
      package.json
  3. 35 49
      src/Line.ts
  4. 15 0
      src/LineConnection.ts
  5. 4 3
      src/Station.ts
  6. 78 51
      src/transport.ts
  7. 1 0
      webpack.config.js

+ 81 - 0
package-lock.json

@@ -980,6 +980,11 @@
980 980
       "resolved": "https://registry.npmjs.org/@types/pixi.js/-/pixi.js-4.7.2.tgz",
981 981
       "integrity": "sha512-ybrqVdncNCa81fCYCqxz/CISyMbXl8usszNv0mwdeYDyfDqmemQHJtf4GtduHva+3suhItPc9Akr/WfV19zWiQ=="
982 982
     },
983
+    "@types/stats.js": {
984
+      "version": "0.17.0",
985
+      "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.0.tgz",
986
+      "integrity": "sha512-9w+a7bR8PeB0dCT/HBULU2fMqf6BAzvKbxFboYhmDtDkKPiyXYbjoe2auwsXlEFI7CFNMF1dCv3dFH5Poy9R1w=="
987
+    },
983 988
     "@types/tinycolor2": {
984 989
       "version": "1.4.0",
985 990
       "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.0.tgz",
@@ -4406,6 +4411,11 @@
4406 4411
         }
4407 4412
       }
4408 4413
     },
4414
+    "exists": {
4415
+      "version": "1.0.1",
4416
+      "resolved": "https://registry.npmjs.org/exists/-/exists-1.0.1.tgz",
4417
+      "integrity": "sha1-/8vuKRQvJAVt8Bkk5zJicz2xC0k="
4418
+    },
4409 4419
     "exit-hook": {
4410 4420
       "version": "1.1.1",
4411 4421
       "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-1.1.1.tgz",
@@ -9205,6 +9215,11 @@
9205 9215
         "sha.js": "2.4.11"
9206 9216
       }
9207 9217
     },
9218
+    "penner": {
9219
+      "version": "0.1.3",
9220
+      "resolved": "https://registry.npmjs.org/penner/-/penner-0.1.3.tgz",
9221
+      "integrity": "sha1-C4tILU6bOa8vPXw3WSIpuKzClwU="
9222
+    },
9208 9223
     "pify": {
9209 9224
       "version": "3.0.0",
9210 9225
       "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz",
@@ -9226,11 +9241,46 @@
9226 9241
         "pinkie": "2.0.4"
9227 9242
       }
9228 9243
     },
9244
+    "pixi-ease": {
9245
+      "version": "0.18.0",
9246
+      "resolved": "https://registry.npmjs.org/pixi-ease/-/pixi-ease-0.18.0.tgz",
9247
+      "integrity": "sha512-qC9ofPKHblNlkdKDFgXDcw1vTPg4zDTLBudk32lScafOB59QKE5duElA+XwaS5kEpumVfnlKtg3k4gCcGcat3Q==",
9248
+      "requires": {
9249
+        "eventemitter3": "3.0.1",
9250
+        "penner": "0.1.3",
9251
+        "yy-angle": "1.2.0",
9252
+        "yy-color": "1.0.7"
9253
+      },
9254
+      "dependencies": {
9255
+        "eventemitter3": {
9256
+          "version": "3.0.1",
9257
+          "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.0.1.tgz",
9258
+          "integrity": "sha512-QOCPu979MMWX9XNlfRZoin+Wm+bK1SP7vv3NGUniYwuSJK/+cPA10blMaeRgzg31RvoSFk6FsCDVa4vNryBTGA=="
9259
+        }
9260
+      }
9261
+    },
9229 9262
     "pixi-gl-core": {
9230 9263
       "version": "1.1.4",
9231 9264
       "resolved": "https://registry.npmjs.org/pixi-gl-core/-/pixi-gl-core-1.1.4.tgz",
9232 9265
       "integrity": "sha1-i0tcQzsx5Bm8N53FZc4bg1qRs3I="
9233 9266
     },
9267
+    "pixi-viewport": {
9268
+      "version": "1.5.0",
9269
+      "resolved": "https://registry.npmjs.org/pixi-viewport/-/pixi-viewport-1.5.0.tgz",
9270
+      "integrity": "sha512-hMPtka90PulpBLXBhE3RZvKaB1VTPFoXe4dSuqsYYBQeo8b1G3FTy7WAfgqkqj1ibrblEf0MmTzO9PzoXKLKXA==",
9271
+      "requires": {
9272
+        "eventemitter3": "3.0.1",
9273
+        "exists": "1.0.1",
9274
+        "pixi-ease": "0.18.0"
9275
+      },
9276
+      "dependencies": {
9277
+        "eventemitter3": {
9278
+          "version": "3.0.1",
9279
+          "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.0.1.tgz",
9280
+          "integrity": "sha512-QOCPu979MMWX9XNlfRZoin+Wm+bK1SP7vv3NGUniYwuSJK/+cPA10blMaeRgzg31RvoSFk6FsCDVa4vNryBTGA=="
9281
+        }
9282
+      }
9283
+    },
9234 9284
     "pixi.js": {
9235 9285
       "version": "4.7.1",
9236 9286
       "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-4.7.1.tgz",
@@ -10595,6 +10645,11 @@
10595 10645
       "integrity": "sha1-o0a7Gs1CB65wvXwMfKnlZra63bg=",
10596 10646
       "dev": true
10597 10647
     },
10648
+    "seedrandom": {
10649
+      "version": "2.4.3",
10650
+      "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-2.4.3.tgz",
10651
+      "integrity": "sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw="
10652
+    },
10598 10653
     "select-hose": {
10599 10654
       "version": "2.0.0",
10600 10655
       "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
@@ -11159,6 +11214,11 @@
11159 11214
         }
11160 11215
       }
11161 11216
     },
11217
+    "stats.js": {
11218
+      "version": "0.17.0",
11219
+      "resolved": "https://registry.npmjs.org/stats.js/-/stats.js-0.17.0.tgz",
11220
+      "integrity": "sha1-scPcRtlEmLV4t/05hbgaznExzH0="
11221
+    },
11162 11222
     "statuses": {
11163 11223
       "version": "1.4.0",
11164 11224
       "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
@@ -12966,6 +13026,27 @@
12966 13026
           "dev": true
12967 13027
         }
12968 13028
       }
13029
+    },
13030
+    "yy-angle": {
13031
+      "version": "1.2.0",
13032
+      "resolved": "https://registry.npmjs.org/yy-angle/-/yy-angle-1.2.0.tgz",
13033
+      "integrity": "sha512-Sf311F5zlItZA0dH/mD3MMG6mIIo1b+9XsFqpLhKCTqFFUL4ip+XWTaQLWLRDkh/Ag0GzAsGt6rWcq61OU9+zQ=="
13034
+    },
13035
+    "yy-color": {
13036
+      "version": "1.0.7",
13037
+      "resolved": "https://registry.npmjs.org/yy-color/-/yy-color-1.0.7.tgz",
13038
+      "integrity": "sha1-P5IxiCQ/q8zqJNNy9Li24wk8Zak=",
13039
+      "requires": {
13040
+        "yy-random": "1.6.0"
13041
+      }
13042
+    },
13043
+    "yy-random": {
13044
+      "version": "1.6.0",
13045
+      "resolved": "https://registry.npmjs.org/yy-random/-/yy-random-1.6.0.tgz",
13046
+      "integrity": "sha512-oMIO8eo4BVed+o8NDMGj9scboHWBFtOdip1oDpXhxYw3A03lYqDqtlwPLg8SOK0q95Fsdzj9EdCQMWFWRk3p7A==",
13047
+      "requires": {
13048
+        "seedrandom": "2.4.3"
13049
+      }
12969 13050
     }
12970 13051
   }
12971 13052
 }

+ 3 - 0
package.json

@@ -44,8 +44,11 @@
44 44
   },
45 45
   "dependencies": {
46 46
     "@types/pixi.js": "^4.7.2",
47
+    "@types/stats.js": "^0.17.0",
47 48
     "@types/tinycolor2": "^1.4.0",
49
+    "pixi-viewport": "^1.5.0",
48 50
     "pixi.js": "^4.7.1",
51
+    "stats.js": "^0.17.0",
49 52
     "tinycolor2": "^1.4.1"
50 53
   }
51 54
 }

+ 35 - 49
src/Line.ts

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

+ 15 - 0
src/LineConnection.ts

@@ -0,0 +1,15 @@
1
+import Line from './Line';
2
+import Station from './Station';
3
+
4
+export default class LineConnection {
5
+  public station: Station;
6
+  public line: Line;
7
+
8
+  constructor(
9
+    station: Station,
10
+    line: Line,
11
+  ) {
12
+    this.station = station;
13
+    this.line = line;
14
+  }
15
+}

+ 4 - 3
src/Station.ts

@@ -1,6 +1,7 @@
1 1
 import * as tinycolor from 'tinycolor2';
2 2
 
3 3
 import Direction, { getPointDirection } from './Direction';
4
+import LineConnection from './LineConnection';
4 5
 import { distance, randomPoint, weightedRandom } from './utils';
5 6
 
6 7
 let stationCount = 0;
@@ -65,7 +66,7 @@ export default class Station {
65 66
 
66 67
   public location: PIXI.Point;
67 68
   public population: number;
68
-  public connections: Station[];
69
+  public connections: LineConnection[];
69 70
   public id: number;
70 71
   public label: PIXI.Text;
71 72
   public color: tinycolorInstance;
@@ -74,12 +75,12 @@ export default class Station {
74 75
     location: PIXI.Point,
75 76
     population: number,
76 77
     color: tinycolorInstance,
77
-    connections?: Station[],
78
+    connections?: LineConnection[],
78 79
   ) {
79 80
     this.location = location;
80 81
     this.population = population;
81 82
     this.color = color;
82
-    this.connections = connections;
83
+    this.connections = connections || [];
83 84
 
84 85
     // for debugging
85 86
     stationCount += 1;

+ 78 - 51
src/transport.ts

@@ -1,4 +1,6 @@
1
+import * as Viewport from 'pixi-viewport';
1 2
 import * as PIXI from 'pixi.js';
3
+import * as Stats from 'stats.js';
2 4
 import * as tinycolor from 'tinycolor2';
3 5
 
4 6
 import Direction from './Direction';
@@ -16,10 +18,14 @@ const NODE_RES = 100;
16 18
 const MAX_SPEED = 10.0;
17 19
 const ACCELERATION = 0.025;
18 20
 const APPROACH_DISTANCE = 3.0;
19
-const MAX_JOURNEY = Math.floor(Math.sqrt(
20
-  Math.pow(window.innerWidth, 2) + Math.pow(window.innerHeight, 2),
21
-) / 4);
22 21
 const TRAIN_CAPACITY = 50;
22
+const LINE_CONNECTION_LIMIT = 5;
23
+const WORLD_WIDTH = 1000;
24
+const WORLD_HEIGHT = 1000;
25
+const ZOOM_MIN_WIDTH = 100;
26
+const ZOOM_MIN_HEIGHT = 100;
27
+const ZOOM_MAX_WIDTH = 4000;
28
+const ZOOM_MAX_HEIGHT = 4000;
23 29
 
24 30
 const trainTexts: PIXI.Text[] = [];
25 31
 
@@ -36,8 +42,11 @@ const initStations = (numStations: number): Station[] => {
36 42
 
37 43
 const initTrains = (numTrains: number, stations: Station[]): Train[] => {
38 44
   const trains = [];
45
+  const stationsWithConnections = stations.filter(station => station.connections.length > 0);
39 46
   for (let i = 0; i < numTrains; i += 1) {
40
-    const originStation = stations[Math.floor(Math.random() * stations.length)];
47
+    const originStation = stationsWithConnections[
48
+      Math.floor(Math.random() * stationsWithConnections.length)
49
+    ];
41 50
     trains.push(new Train(
42 51
       new PIXI.Point(originStation.location.x, originStation.location.y),
43 52
       0, 0, originStation, undefined, tinycolor('grey')),
@@ -46,15 +55,36 @@ const initTrains = (numTrains: number, stations: Station[]): Train[] => {
46 55
   return trains;
47 56
 };
48 57
 
58
+const initLines = (numLines: number, stations: Station[]): Line[] => {
59
+  const lines = [];
60
+  for (let i = 0; i < numLines; i += 1) {
61
+    let color = tinycolor.random();
62
+    while (color.isDark()) {
63
+      color = tinycolor.random();
64
+    }
65
+    const stationsWithoutConnections = stations.filter(station =>
66
+      station.connections.length === 0,
67
+    );
68
+    const centralHub = Station.largestStation(stationsWithoutConnections);
69
+    const line = new Line(`line-${i}`, tinycolor.random());
70
+    const stationsLeft = stations.slice(0);
71
+    line.connectStations(centralHub, stationsLeft, [], LINE_CONNECTION_LIMIT);
72
+    lines.push(line);
73
+  }
74
+  return lines;
75
+};
76
+
49 77
 const moveTrains = (trains: Train[], stations: Station[]) => {
50 78
   for (const train of trains) {
79
+    if (train.origin.connections.length === 0) {
80
+      // train is stuck at an orphaned station
81
+      continue;
82
+    }
51 83
     // choose a destination randomly with a bias towards larger stations
52 84
     if (train.destination === undefined) {
53
-      const otherStations = stations.filter(station => station !== train.origin);
54
-      const closeStations = Station.stationsWithinRadius(otherStations, train.location,
55
-                                                         MAX_JOURNEY);
56
-      const closeStationWeights = closeStations.map(station => station.population);
57
-      train.destination = weightedRandom(closeStations, closeStationWeights);
85
+      const otherStations = train.origin.connections.map(conn => conn.station);
86
+      const closeStationWeights = otherStations.map(station => station.population);
87
+      train.destination = weightedRandom(otherStations, closeStationWeights);
58 88
 
59 89
       // board passengers
60 90
       const boardingPassengers = randomInt(0, Math.min(TRAIN_CAPACITY - train.passengers,
@@ -116,7 +146,7 @@ const moveTrains = (trains: Train[], stations: Station[]) => {
116 146
 
117 147
 const drawStations = (stations: Station[], graphics: PIXI.Graphics) => {
118 148
   for (const station of stations) {
119
-    const radius = station.population / 60;
149
+    const radius = station.population / 150;
120 150
     graphics.beginFill(parseInt(station.color.toHex(), 16), 0.5);
121 151
     graphics.drawCircle(station.location.x, station.location.y, radius);
122 152
     graphics.endFill();
@@ -127,10 +157,6 @@ const drawStations = (stations: Station[], graphics: PIXI.Graphics) => {
127 157
 
128 158
 const drawTrains = (trains: Train[], graphics: PIXI.Graphics) => {
129 159
   for (const train of trains) {
130
-    // graphics.beginFill(parseInt(train.color.toHex(), 16), 0.8);
131
-    // graphics.drawCircle(train.location.x, train.location.y,
132
-                        // rangeMap(train.passengers, 0, TRAIN_CAPACITY, 1, 5));
133
-    // graphics.endFill();
134 160
     const trainSize = rangeMap(train.passengers, 0, TRAIN_CAPACITY, 1, 5);
135 161
     const scale = trainSize / NODE_RES;
136 162
     train.sprite.x = train.location.x;
@@ -143,13 +169,18 @@ const drawTrains = (trains: Train[], graphics: PIXI.Graphics) => {
143 169
   }
144 170
 };
145 171
 
146
-const drawLines = (lines: Line[], graphics: PIXI.Graphics) => {
147
-  for (const line of lines) {
148
-    graphics.lineStyle(1, parseInt(line.color.toHex(), 16), 1);
149
-    const start = line.stations[0].location;
150
-    graphics.moveTo(start.x, start.y);
151
-    for (const station of line.stations.slice(1)) {
152
-      graphics.lineTo(station.location.x, station.location.y);
172
+const drawLines = (stations: Station[], graphics: PIXI.Graphics) => {
173
+  for (const station of stations) {
174
+    for (const connection of station.connections) {
175
+      let twoWay = false;
176
+      for (const conn of connection.station.connections) {
177
+        if (conn.station === station) {
178
+          twoWay = true;
179
+        }
180
+      }
181
+      graphics.lineStyle(twoWay ? 2 : 1, parseInt(connection.line.color.toHex(), 16), 1);
182
+      graphics.moveTo(station.location.x, station.location.y);
183
+      graphics.lineTo(connection.station.location.x, connection.station.location.y);
153 184
     }
154 185
   }
155 186
 };
@@ -160,41 +191,29 @@ const run = () => {
160 191
     height: window.innerHeight,
161 192
     width: window.innerWidth,
162 193
   });
194
+  const viewport = new Viewport({
195
+    screenHeight: window.innerHeight,
196
+    screenWidth: window.innerWidth,
197
+    worldHeight: WORLD_HEIGHT,
198
+    worldWidth: WORLD_WIDTH,
199
+  });
200
+  const stats = new Stats();
201
+  stats.showPanel(0);
202
+  document.body.appendChild(stats.dom);
163 203
   const ticker = new PIXI.ticker.Ticker();
164 204
   const graphics = new PIXI.Graphics();
165
-  const fpsText = new PIXI.Text('', { fontSize: '25px', fontFamily: 'monospace', fill: 'yellow' });
166
-
167
-  fpsText.anchor = new PIXI.ObservablePoint(null, 0, 1);
168
-  fpsText.x = window.innerWidth;
169
-  fpsText.y = 0;
170 205
 
171 206
   const stations = initStations(30);
207
+  const lines = initLines(4, stations);
172 208
   const trains = initTrains(50, stations);
173
-  const lines = [
174
-    new Line(
175
-      stations, new PIXI.Point(0, 0),
176
-      Direction.Southeast, 12, tinycolor('red'),
177
-    ),
178
-    new Line(
179
-      stations, new PIXI.Point(window.innerWidth, 0),
180
-      Direction.Southwest, 12, tinycolor('darkcyan'),
181
-    ),
182
-    new Line(
183
-      stations, new PIXI.Point(window.innerWidth, window.innerHeight),
184
-      Direction.Northwest, 12, tinycolor('yellow'),
185
-    ),
186
-    new Line(
187
-      stations, new PIXI.Point(0, window.innerHeight),
188
-      Direction.Northeast, 12, tinycolor('green'),
189
-    ),
190
-  ];
191 209
 
192 210
   ticker.stop();
193 211
   ticker.add((deltaTime) => {
212
+    stats.begin();
213
+
194 214
     moveTrains(trains, stations);
195 215
 
196 216
     graphics.clear();
197
-    fpsText.text = `${Math.round(ticker.FPS)}`;
198 217
 
199 218
     graphics.lineStyle(1, 0xFFA500, 1);
200 219
     drawStations(stations, graphics);
@@ -202,24 +221,32 @@ const run = () => {
202 221
     graphics.lineStyle(1, 0xAEAEAE, 1);
203 222
     drawTrains(trains, graphics);
204 223
 
205
-    drawLines(lines, graphics);
224
+    drawLines(stations, graphics);
225
+
226
+    stats.end();
206 227
   });
207 228
   ticker.start();
208 229
 
209
-  app.stage.addChild(graphics);
210
-  app.stage.addChild(fpsText);
230
+  viewport.addChild(graphics);
211 231
   // add train sprites
212 232
   for (const train of trains) {
213
-    app.stage.addChild(train.sprite);
233
+    viewport.addChild(train.sprite);
214 234
   }
215 235
   // Add debug labels
216 236
   for (const train of trains) {
217
-    app.stage.addChild(train.label);
237
+    viewport.addChild(train.label);
218 238
   }
219 239
   for (const station of stations) {
220
-    app.stage.addChild(station.label);
240
+    viewport.addChild(station.label);
221 241
   }
222 242
   document.body.appendChild(app.view);
243
+  app.stage.addChild(viewport);
244
+  viewport.drag().pinch().wheel().clampZoom({
245
+    maxHeight: ZOOM_MAX_HEIGHT,
246
+    maxWidth: ZOOM_MAX_WIDTH,
247
+    minHeight: ZOOM_MIN_HEIGHT,
248
+    minWidth: ZOOM_MIN_WIDTH,
249
+  }).decelerate();
223 250
 
224 251
   window.addEventListener('resize', () => {
225 252
     app.renderer.resize(window.innerWidth, window.innerHeight);

+ 1 - 0
webpack.config.js

@@ -10,6 +10,7 @@ module.exports = {
10 10
     filename: 'transport.js',
11 11
     path: path.resolve(__dirname, 'dist'),
12 12
   },
13
+  mode: env === 'production' ? 'production' : 'development',
13 14
   module: {
14 15
     rules: [
15 16
       {