diff --git a/package-lock.json b/package-lock.json index 0ee6ad5..87d3a98 100644 --- a/package-lock.json +++ b/package-lock.json @@ -375,6 +375,17 @@ "@babel/plugin-syntax-async-generators": "7.0.0-beta.42" } }, + "@babel/plugin-proposal-class-properties": { + "version": "7.0.0-beta.42", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.0.0-beta.42.tgz", + "integrity": "sha512-VXRmo/t2nAVciXxEgPTAfcUBXj0UXNPIvX2aj3lzHL51N+uh+rtgsIF0nuZwGE4u89WvBDH66yjAu60Ra674kw==", + "dev": true, + "requires": { + "@babel/helper-function-name": "7.0.0-beta.42", + "@babel/helper-plugin-utils": "7.0.0-beta.42", + "@babel/plugin-syntax-class-properties": "7.0.0-beta.42" + } + }, "@babel/plugin-proposal-object-rest-spread": { "version": "7.0.0-beta.42", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.0.0-beta.42.tgz", @@ -446,6 +457,15 @@ "@babel/helper-plugin-utils": "7.0.0-beta.42" } }, + "@babel/plugin-syntax-class-properties": { + "version": "7.0.0-beta.42", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.0.0-beta.42.tgz", + "integrity": "sha512-nG0XCeuni6GgjxOqtxVtnfSoRFeXdqY6cja83cmFpC1klw8f6XShbeDmK7xX1mUYBHkEqLTDurlX+fuua9siCg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "7.0.0-beta.42" + } + }, "@babel/plugin-syntax-object-rest-spread": { "version": "7.0.0-beta.42", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.0.0-beta.42.tgz", @@ -927,12 +947,39 @@ } } }, + "@fimbul/bifrost": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@fimbul/bifrost/-/bifrost-0.6.0.tgz", + "integrity": "sha512-BJ19rjnFFCeopEhbyK2Chg3Tq+o5xkjd6dtKxmFhfjwLH1Il2G7Ha4Jel2hpbyZL2Fh9/vrM9U0bpkANAL3pjA==", + "dev": true, + "requires": { + "@fimbul/ymir": "0.6.0", + "get-caller-file": "1.0.2", + "tslib": "1.9.0" + } + }, + "@fimbul/ymir": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@fimbul/ymir/-/ymir-0.6.0.tgz", + "integrity": "sha512-iyh/8OiZlzjlPytdjdodA86d38YtRL0sSAx169SMgqP4dsouH2rtctf4Nrg4FYvWoG0e9y9XT3iWL+mjTgYNRw==", + "dev": true, + "requires": { + "inversify": "4.11.1", + "reflect-metadata": "0.1.12", + "tslib": "1.9.0" + } + }, "@sindresorhus/is": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz", "integrity": "sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==", "dev": true }, + "@types/pixi.js": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/@types/pixi.js/-/pixi.js-4.7.2.tgz", + "integrity": "sha512-ybrqVdncNCa81fCYCqxz/CISyMbXl8usszNv0mwdeYDyfDqmemQHJtf4GtduHva+3suhItPc9Akr/WfV19zWiQ==" + }, "accepts": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", @@ -6801,6 +6848,12 @@ "loose-envify": "1.3.1" } }, + "inversify": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/inversify/-/inversify-4.11.1.tgz", + "integrity": "sha512-9bs/36crPdTSOCcoomHMb96s+B8W0+2c9dHFP/Srv9ZQaPnUvsMgzmMHfgVECqfHVUIW+M5S7SYOjoig8khWuQ==", + "dev": true + }, "invert-kv": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", @@ -10156,6 +10209,12 @@ } } }, + "reflect-metadata": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.12.tgz", + "integrity": "sha512-n+IyV+nGz3+0q3/Yf1ra12KpCyi001bi4XFxSjbiWWjfqb52iTTtpGXmCCAOWWIAn9KEuFZKGqBERHmrtScZ3A==", + "dev": true + }, "regenerate": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.3.3.tgz", @@ -11509,6 +11568,126 @@ "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", "dev": true }, + "ts-loader": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-4.1.0.tgz", + "integrity": "sha512-R4VEE0SZhU8gLa9Aayg2XOvpVjXtbB7KMLXs4He0xr92DM5G12i76IWd+lMLfmCz66Ztr2XFvDNvMAymHJGIqg==", + "dev": true, + "requires": { + "chalk": "2.3.2", + "enhanced-resolve": "4.0.0", + "loader-utils": "1.1.0", + "micromatch": "3.1.10", + "semver": "5.5.0" + } + }, + "tslib": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.0.tgz", + "integrity": "sha512-f/qGG2tUkrISBlQZEjEqoZ3B2+npJjIf04H1wuAv9iA8i04Icp+61KRXxFdha22670NJopsZCIjhC3SnjPRKrQ==", + "dev": true + }, + "tslint": { + "version": "5.9.1", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.9.1.tgz", + "integrity": "sha1-ElX4ej/1frCw4fDmEKi0dIBGya4=", + "dev": true, + "requires": { + "babel-code-frame": "6.26.0", + "builtin-modules": "1.1.1", + "chalk": "2.3.2", + "commander": "2.13.0", + "diff": "3.5.0", + "glob": "7.1.2", + "js-yaml": "3.11.0", + "minimatch": "3.0.4", + "resolve": "1.6.0", + "semver": "5.5.0", + "tslib": "1.9.0", + "tsutils": "2.25.0" + } + }, + "tslint-config-airbnb": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/tslint-config-airbnb/-/tslint-config-airbnb-5.8.0.tgz", + "integrity": "sha512-4fTcxL/3Xbct8BIt1jjZvp9EavqwUO7ND9mpRsdHmvdAYGCbGdaIaUg9NCB6VXLDtTBTR5XT/tEU3/jBN/JPAw==", + "dev": true, + "requires": { + "tslint-consistent-codestyle": "1.13.0", + "tslint-eslint-rules": "4.1.1", + "tslint-microsoft-contrib": "5.0.3" + } + }, + "tslint-consistent-codestyle": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslint-consistent-codestyle/-/tslint-consistent-codestyle-1.13.0.tgz", + "integrity": "sha512-7fcstphFz9Rw2+SAe32VjtnQEHYEQVSGgEOea9hN/8JMJQGpGkxvVbqxhsXew9vkRtvPQuoj1pQoZ5Eadp4B6A==", + "dev": true, + "requires": { + "@fimbul/bifrost": "0.6.0", + "tslib": "1.9.0", + "tsutils": "2.25.0" + } + }, + "tslint-eslint-rules": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tslint-eslint-rules/-/tslint-eslint-rules-4.1.1.tgz", + "integrity": "sha1-fDDniC8mvCdr/5HSOEl1xp2viLo=", + "dev": true, + "requires": { + "doctrine": "0.7.2", + "tslib": "1.9.0", + "tsutils": "1.9.1" + }, + "dependencies": { + "doctrine": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-0.7.2.tgz", + "integrity": "sha1-fLhgNZujvpDgQLJrcpzkv6ZUxSM=", + "dev": true, + "requires": { + "esutils": "1.1.6", + "isarray": "0.0.1" + } + }, + "esutils": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-1.1.6.tgz", + "integrity": "sha1-wBzKqa5LiXxtDD4hCuUvPHqEQ3U=", + "dev": true + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "tsutils": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-1.9.1.tgz", + "integrity": "sha1-ufmrROVa+WgYMdXyjQrur1x1DLA=", + "dev": true + } + } + }, + "tslint-microsoft-contrib": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-5.0.3.tgz", + "integrity": "sha512-5AnfTGlfpUzpRHLmoojPBKFTTmbjnwgdaTHMdllausa4GBPya5u36i9ddrTX4PhetGZvd4JUYIpAmgHqVnsctg==", + "dev": true, + "requires": { + "tsutils": "2.25.0" + } + }, + "tsutils": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.25.0.tgz", + "integrity": "sha512-SPgUlOAUAe6fCyPi0QR4U0jRuDsHHKvzIR6/hHd0YR0bb8MzeLJgCagkPSmZeJjWImnpJ0xq6XHa9goTvMBBCQ==", + "dev": true, + "requires": { + "tslib": "1.9.0" + } + }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", @@ -11540,6 +11719,12 @@ "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", "dev": true }, + "typescript": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.8.1.tgz", + "integrity": "sha512-Ao/f6d/4EPLq0YwzsQz8iXflezpTkQzqAyenTiw4kCUGr1uPiFLC3+fZ+gMZz6eeI/qdRUqvC+HxIJzUAzEFdg==", + "dev": true + }, "uglify-es": { "version": "3.3.9", "resolved": "https://registry.npmjs.org/uglify-es/-/uglify-es-3.3.9.tgz", diff --git a/package.json b/package.json index 6ae8a1d..7630998 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "start": "NODE_ENV=development ./node_modules/.bin/webpack-dev-server --mode development", "build": "NODE_ENV=production ./node_modules/.bin/webpack --mode production", "deploy": "./node_modules/.bin/gh-pages -d dist", - "lint": "./node_modules/.bin/eslint src/", + "lint": "./node_modules/.bin/tslint src/", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -18,6 +18,8 @@ "license": "ISC", "devDependencies": { "@babel/core": "^7.0.0-beta.42", + "@babel/plugin-proposal-class-properties": "^7.0.0-beta.42", + "@babel/plugin-proposal-object-rest-spread": "^7.0.0-beta.42", "@babel/preset-env": "^7.0.0-beta.42", "babel-loader": "^8.0.0-beta.2", "css-loader": "^0.28.11", @@ -31,11 +33,16 @@ "gh-pages": "^1.1.0", "html-webpack-plugin": "^3.1.0", "style-loader": "^0.20.3", + "ts-loader": "^4.1.0", + "tslint": "^5.9.1", + "tslint-config-airbnb": "^5.8.0", + "typescript": "^2.8.1", "webpack": "^4.4.1", "webpack-cli": "^2.0.13", "webpack-dev-server": "^3.1.1" }, "dependencies": { + "@types/pixi.js": "^4.7.2", "pixi.js": "^4.7.1" } } diff --git a/src/Line.ts b/src/Line.ts new file mode 100644 index 0000000..48ccd3b --- /dev/null +++ b/src/Line.ts @@ -0,0 +1,42 @@ +import Station from './Station'; +import { distance, randomInt, randomPoint } from './utils'; + +const largestStation = (stations: Station[]): Station => { + let largest: Station = null; + for (const station of stations) { + if (largest === null || station.population > largest.population) { + largest = station; + } + } + return largest; +}; + +const stationsWithinRadius = (stations: Station[], point: PIXI.Point, + radius: number): Station[] => ( + stations.filter(station => distance(point, station.location) <= radius) +); + +const closestStations = (stations: Station[], point: PIXI.Point, num: number): Station[] => { + // bleh, i'm done + return stations; +}; + +export default class Line { + public stations: Station[]; + + constructor(stations: Station[], numStations: number) { + this.stations = []; + let stationsLeft = stations; + let largest = largestStation(stationsLeft); + stationsLeft = stationsLeft.filter(s => s !== largest); + this.stations.push(largest); + while (this.stations.length < numStations) { + largest = largestStation(stationsWithinRadius(stationsLeft, largest.location, 500)); + if (largest === null) { + break; + } + stationsLeft = stationsLeft.filter(s => s !== largest); + this.stations.push(largest); + } + } +} diff --git a/src/Station.ts b/src/Station.ts new file mode 100644 index 0000000..3cdf723 --- /dev/null +++ b/src/Station.ts @@ -0,0 +1,11 @@ +export default class Station { + public location: PIXI.Point; + public population: number; + public connections: Station[]; + + constructor(location: PIXI.Point, population: number, connections?: Station[]) { + this.location = location; + this.population = population; + this.connections = connections; + } +} diff --git a/src/Train.ts b/src/Train.ts new file mode 100644 index 0000000..4fa4333 --- /dev/null +++ b/src/Train.ts @@ -0,0 +1,23 @@ +import Station from './Station'; + +export default class Train { + public location: PIXI.Point; + public speed: number; + public origin: Station; + public destination: Station; + public passengers: number; + + constructor(location: PIXI.Point, speed: number, passengers: number, origin: Station, + destination: Station) { + this.location = location; + this.speed = speed; + this.origin = origin; + this.destination = destination; + this.passengers = passengers; + } + + public boardPassengers() { + if (this.location === this.origin.location) { // about to leave a station + } + } +} diff --git a/src/transport.js b/src/transport.js deleted file mode 100644 index 81e0318..0000000 --- a/src/transport.js +++ /dev/null @@ -1,30 +0,0 @@ -import * as PIXI from 'pixi.js'; -import { randomInt } from './utils'; -import './style.css'; - -const app = new PIXI.Application(window.innerWidth, window.innerHeight); -const ticker = new PIXI.ticker.Ticker(); -const graphics = new PIXI.Graphics(); -const fpsText = new PIXI.Text('', { fontSize: '25px', fontFamily: 'monospace', fill: 'yellow' }); - -fpsText.anchor = new PIXI.Point(1, 0); -fpsText.x = window.innerWidth - 1; -fpsText.y = 0; - -ticker.stop(); -ticker.add((deltaTime) => { - fpsText.setText(Math.round(ticker.FPS)); - graphics.lineStyle(1, 0xaeaeae, 1); - - graphics.moveTo(randomInt(9, window.innerWidth), randomInt(0, window.innerHeight)); - graphics.lineTo(randomInt(9, window.innerWidth), randomInt(0, window.innerHeight)); -}); -ticker.start(); - -app.stage.addChild(graphics); -app.stage.addChild(fpsText); -document.body.appendChild(app.view); - -window.addEventListener('resize', () => { - app.renderer.resize(window.innerWidth, window.innerHeight); -}); diff --git a/src/transport.ts b/src/transport.ts new file mode 100644 index 0000000..afe8149 --- /dev/null +++ b/src/transport.ts @@ -0,0 +1,106 @@ +import * as PIXI from 'pixi.js'; +import Line from './Line'; +import Station from './Station'; +import Train from './Train'; +import { distance, randomInt, randomPoint } from './utils'; + +import './style.css'; + +const isPointDistant = (point: PIXI.Point, stations: Station[], minDistance: number): boolean => { + for (const station of stations) { + if (distance(point, station.location) < minDistance) { + return false; + } + } + return true; +}; + +const randomDistantPoint = (stations: Station[], minDistance: number): PIXI.Point | null => { + let tries = 100; + while (tries > 0) { + const point = randomPoint(); + if (isPointDistant(point, stations, minDistance)) { + return point; + } + tries -= 1; + } + return null; +}; + +const initStations = (numStations: number): Station[] => { + const stations: Station[] = []; + for (let i = 0; i < numStations; i += 1) { + stations.push(new Station(randomDistantPoint(stations, 30), randomInt(100, 1000))); + } + return stations; +}; + +const drawStations = (stations: Station[], graphics: PIXI.Graphics) => { + for (const station of stations) { + graphics.drawCircle(station.location.x, station.location.y, station.population / 60); + } +}; + +const initTrains = (numTrains: number, stations: Station[]): Train[] => { + const trains = []; + for (let i = 0; i < numTrains; i += 1) { + const originStation = stations[Math.floor(Math.random() * stations.length)]; + const destStation = stations[Math.floor(Math.random() * stations.length)]; + trains.push(new Train(originStation.location, 0, 0, originStation, destStation)); + } + return trains; +}; + +const drawLine = (line: Line, graphics: PIXI.Graphics) => { + const start = line.stations[0].location; + graphics.moveTo(start.x, start.y); + for (const station of line.stations.slice(1)) { + graphics.lineTo(station.location.x, station.location.y); + } +}; + +const run = () => { + const app = new PIXI.Application({ + antialias: true, + height: window.innerHeight, + width: window.innerWidth, + }); + const ticker = new PIXI.ticker.Ticker(); + const graphics = new PIXI.Graphics(); + const fpsText = new PIXI.Text('', { fontSize: '25px', fontFamily: 'monospace', fill: 'yellow' }); + + fpsText.anchor = new PIXI.ObservablePoint(null, 0, 1); + fpsText.x = window.innerWidth; + fpsText.y = 0; + + // make these const + let stations = initStations(30); + let trains = initTrains(15, stations); + let line = new Line(stations, 10); + + stations = initStations(30); + trains = initTrains(15, stations); + line = new Line(stations, 10); + + ticker.stop(); + ticker.add((deltaTime) => { + + graphics.clear(); + fpsText.text = `${Math.round(ticker.FPS)}`; + graphics.lineStyle(1, 0xaeaeae, 1); + + drawStations(stations, graphics); + drawLine(line, graphics); + }); + ticker.start(); + + app.stage.addChild(graphics); + app.stage.addChild(fpsText); + document.body.appendChild(app.view); + + window.addEventListener('resize', () => { + app.renderer.resize(window.innerWidth, window.innerHeight); + }); +}; + +run(); diff --git a/src/utils.js b/src/utils.js deleted file mode 100644 index 1bbc108..0000000 --- a/src/utils.js +++ /dev/null @@ -1,4 +0,0 @@ -export const randomInt = (min, max) => ( - // inclusive of min and max - Math.floor(Math.random() * (max - (min + 1))) + min -); diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..94d1723 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,16 @@ +import * as PIXI from 'pixi.js'; + +export const randomInt = (min: number, max: number): number => ( + // inclusive of min and max + Math.floor(Math.random() * (max - (min + 1))) + min +); + +export const randomPoint = () => ( + new PIXI.Point(randomInt(0, window.innerWidth), randomInt(0, window.innerHeight)) +); + +export const distance = (pointA: PIXI.Point, pointB: PIXI.Point): number => { + const distX = pointA.x - pointB.x; + const distY = pointA.y - pointB.y; + return Math.sqrt((distX * distX) + (distY * distY)); +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..96f97d7 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "outDir": "./dist/", + "sourceMap": true, + "noImplicitAny": true, + "module": "es6", + "target": "es5", + "jsx": "react", + "allowJs": true + } +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 0000000..b53aac2 --- /dev/null +++ b/tslint.json @@ -0,0 +1,11 @@ +{ + "defaultSeverity": "error", + "extends": [ + "tslint:recommended", + "tslint-config-airbnb", + "tslint-eslint-rules" + ], + "jsRules": {}, + "rules": {}, + "rulesDirectory": [] +} diff --git a/webpack.config.js b/webpack.config.js index 42baf86..8a73d91 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -5,7 +5,7 @@ const HtmlWebpackPlugin = require('html-webpack-plugin'); const env = process.env.NODE_ENV; module.exports = { - entry: './src/transport.js', + entry: './src/transport.ts', output: { filename: 'transport.js', path: path.resolve(__dirname, 'dist'), @@ -18,10 +18,22 @@ module.exports = { use: { loader: 'babel-loader', options: { - presets: ['@babel/preset-env'], + presets: [ + '@babel/preset-env', + ], + plugins: [ + '@babel/proposal-class-properties', + '@babel/proposal-object-rest-spread', + ], + cacheDirectory: env === 'development', }, }, }, + { + test: /\.tsx?$/, + use: 'ts-loader', + exclude: /node_modules/, + }, { test: /\.css$/, use: env === 'production' @@ -33,6 +45,9 @@ module.exports = { }, ], }, + resolve: { + extensions: ['.tsx', '.ts', '.js'], + }, plugins: [ new HtmlWebpackPlugin({ template: path.join(__dirname, 'index.html'),