From 4d1ef845685bdebc2058d42f2faa7514530641a0 Mon Sep 17 00:00:00 2001 From: Tyler Hallada Date: Sun, 22 Apr 2018 18:40:20 -0400 Subject: [PATCH] Try initializing and move trains in Web Workers Unfortunately, all of the train data has to be copied from the train web worker to the main thread on every tick so it knows the new positions of the trains to render. So every few ticks, the GC causes a little lag as it collects ~90+MB of obsolete train arrays. There's no way for the two threads to share the same data thanks to Spectre. --- package-lock.json | 10 ++ package.json | 3 +- src/Line.ts | 12 +- src/LineConnection.ts | 4 +- src/Station.ts | 41 +++---- src/Station.worker.ts | 60 ++++++++++ src/Train.ts | 28 +---- src/Train.worker.ts | 116 +++++++++++++++++++ src/transport.ts | 260 ++++++++++++++++-------------------------- src/utils.ts | 16 +-- typings/custom.d.ts | 16 +++ 11 files changed, 340 insertions(+), 226 deletions(-) create mode 100644 src/Station.worker.ts create mode 100644 src/Train.worker.ts diff --git a/package-lock.json b/package-lock.json index 793cf11..f8e2b23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12769,6 +12769,16 @@ "errno": "0.1.7" } }, + "worker-loader": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/worker-loader/-/worker-loader-1.1.1.tgz", + "integrity": "sha512-qJZLVS/jMCBITDzPo/RuweYSIG8VJP5P67mP/71alGyTZRe1LYJFdwLjLalY3T5ifx0bMDRD3OB6P2p1escvlg==", + "dev": true, + "requires": { + "loader-utils": "1.1.0", + "schema-utils": "0.4.5" + } + }, "wrap-ansi": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", diff --git a/package.json b/package.json index 467b7a2..3072af3 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "uglifyjs-webpack-plugin": "^1.2.4", "webpack": "^4.4.1", "webpack-cli": "^2.0.13", - "webpack-dev-server": "^3.1.1" + "webpack-dev-server": "^3.1.1", + "worker-loader": "^1.1.1" }, "dependencies": { "@types/pixi.js": "^4.7.2", diff --git a/src/Line.ts b/src/Line.ts index a45a314..af29bff 100644 --- a/src/Line.ts +++ b/src/Line.ts @@ -5,17 +5,13 @@ import LineConnection from './LineConnection'; import Station from './Station'; import { distance, randomInt, randomPoint } from './utils'; -const CONNECTION_RADIUS = Math.floor(Math.sqrt( - Math.pow(window.innerWidth, 2) + Math.pow(window.innerHeight, 2), -) / 8); - export default class Line { public name: string; - public color: tinycolorInstance; + public color: ColorFormats.RGBA; constructor( name: string, - color: tinycolorInstance, + color: ColorFormats.RGBA, ) { this.name = name; this.color = color; @@ -26,13 +22,14 @@ export default class Line { stations: Station[], visitedStations: Station[], connectionLimit: number, + connectionRadius: number, ) { visitedStations.push(currentStation); const otherStations = stations.filter(station => station !== currentStation); const closeStations = Station.stationsWithinRadius( otherStations, currentStation.location, - CONNECTION_RADIUS, + connectionRadius, ); for (let i = 0; i < connectionLimit; i += 1) { if (closeStations.length < 1) { @@ -51,6 +48,7 @@ export default class Line { stations, visitedStations, connectionLimit, + connectionRadius, ); } } diff --git a/src/LineConnection.ts b/src/LineConnection.ts index 33db81c..ee9393e 100644 --- a/src/LineConnection.ts +++ b/src/LineConnection.ts @@ -1,9 +1,11 @@ import Line from './Line'; +import Signal from './Signal'; import Station from './Station'; export default class LineConnection { - public station: Station; public line: Line; + public signals: Signal[]; + public station: Station; constructor( station: Station, diff --git a/src/Station.ts b/src/Station.ts index d1780ad..914d0ea 100644 --- a/src/Station.ts +++ b/src/Station.ts @@ -1,3 +1,4 @@ +import Point from 'pixi.js/lib/core/math/Point'; import * as tinycolor from 'tinycolor2'; import Direction, { getPointDirection } from './Direction'; @@ -18,23 +19,23 @@ export default class Station { return largest; } - public static stationsWithinRadius(stations: Station[], point: PIXI.Point, + public static stationsWithinRadius(stations: Station[], point: Point, radius: number): Station[] { return stations.filter(station => distance(point, station.location) <= radius); } - public static stationsInDirection(stations: Station[], point: PIXI.Point, + public static stationsInDirection(stations: Station[], point: Point, direction: Direction): Station[] { return stations.filter(station => getPointDirection(point, station.location) === direction); } - public static closestStation(stations: Station[], point: PIXI.Point): Station { + public static closestStation(stations: Station[], point: Point): Station { return stations.reduce( (prev, curr) => distance(point, prev.location) < distance(point, curr.location) ? prev : curr, ); } - public static randomCloseLargeStation(stations: Station[], point: PIXI.Point, + public static randomCloseLargeStation(stations: Station[], point: Point, radius: number): Station { const closeStations = Station.stationsWithinRadius(stations, point, radius); @@ -42,7 +43,7 @@ export default class Station { return weightedRandom(closeStations, closeStationWeights); } - public static isPointDistant(point: PIXI.Point, stations: Station[], + public static isPointDistant(point: Point, stations: Station[], minDistance: number): boolean { for (const station of stations) { if (distance(point, station.location) < minDistance) { @@ -52,10 +53,15 @@ export default class Station { return true; } - public static randomDistantPoint(stations: Station[], minDistance: number): PIXI.Point | null { + public static randomDistantPoint( + stations: Station[], + minDistance: number, + height: number, + width: number, + ): Point | null { let tries = 100; while (tries > 0) { - const point = randomPoint(); + const point = randomPoint(height, width); if (Station.isPointDistant(point, stations, minDistance)) { return point; } @@ -64,19 +70,16 @@ export default class Station { return null; } - public location: PIXI.Point; + public location: Point; public population: number; public connections: LineConnection[]; public id: number; - public label: PIXI.Text; - public color: tinycolorInstance; - - private textStyle: object; + public color: ColorFormats.RGBA; constructor( - location: PIXI.Point, + location: Point, population: number, - color: tinycolorInstance, + color: ColorFormats.RGBA, connections?: LineConnection[], ) { this.location = location; @@ -87,15 +90,5 @@ export default class Station { // for debugging stationCount += 1; this.id = stationCount; - this.textStyle = { - fill: '#FFA500', - fontFamily: 'monospace', - fontSize: '12px', - }; - this.renderLabel(); - } - - public renderLabel() { - this.label = new PIXI.Text(`${this.id}`, this.textStyle); } } diff --git a/src/Station.worker.ts b/src/Station.worker.ts new file mode 100644 index 0000000..427748c --- /dev/null +++ b/src/Station.worker.ts @@ -0,0 +1,60 @@ +import * as tinycolor from 'tinycolor2'; + +import Line from './Line'; +import Station from './Station'; +import { randomInt } from './utils'; + +const LINE_CONNECTION_LIMIT = 5; +const ctx: Worker = self as any; + +const initStations = (numStations: number, height: number, width: number): Station[] => { + const stations: Station[] = []; + for (let i = 0; i < numStations; i += 1) { + stations.push(new Station( + Station.randomDistantPoint(stations, 30, height, width), + randomInt(300, 2000), + tinycolor.random().toRgb())); + } + return stations; +}; + +const initLines = (numLines: number, stations: Station[], connectionRadius: number): Line[] => { + const lines = []; + for (let i = 0; i < numLines; i += 1) { + let color = tinycolor.random(); + while (color.isDark()) { + color = tinycolor.random(); + } + const lineColor = color.toRgb(); + const stationsWithoutConnections = stations.filter(station => + station.connections.length === 0, + ); + let centralHub: Station; + if (stationsWithoutConnections.length > 0) { + centralHub = Station.largestStation(stationsWithoutConnections); + } else { + centralHub = stations[randomInt(0, stations.length - 1)]; + } + const line = new Line(`line-${i}`, lineColor); + const stationsLeft = stations.slice(0); + line.connectStations(centralHub, stationsLeft, [], LINE_CONNECTION_LIMIT, connectionRadius); + lines.push(line); + } + return lines; +}; + +ctx.addEventListener('message', (event: MessageEvent) => { + if ('initStations' in event.data) { + const { connectionRadius, height, numLines, numStations, width } = event.data.initStations; + let stations: Station[] = []; + let lines: Line[] = []; + let stationsWithConnections: Station[] = []; + while (stationsWithConnections.length === 0) { + // If all stations are too far away to connect, try generating again + stations = initStations(numStations, height, width); + lines = initLines(numLines, stations, connectionRadius); + stationsWithConnections = stations.filter(station => station.connections.length > 0); + } + ctx.postMessage({ stations, lines }); + } +}); diff --git a/src/Train.ts b/src/Train.ts index d3dcf21..6a32a42 100644 --- a/src/Train.ts +++ b/src/Train.ts @@ -1,3 +1,4 @@ +import Point from 'pixi.js/lib/core/math/Point'; import * as tinycolor from 'tinycolor2'; import Station from './Station'; @@ -5,25 +6,23 @@ import Station from './Station'; let trainCount = 0; export default class Train { - public location: PIXI.Point; + public location: Point; public speed: number; public origin: Station; public destination: Station; public passengers: number; public id: number; - public label: PIXI.Text; - public color: tinycolorInstance; - public sprite: PIXI.Sprite; + public color: ColorFormats.RGBA; private textStyle: object; constructor( - location: PIXI.Point, + location: Point, speed: number, passengers: number, origin: Station, destination: Station, - color: tinycolorInstance, + color: ColorFormats.RGBA, ) { this.location = location; this.speed = speed; @@ -32,25 +31,8 @@ export default class Train { this.passengers = passengers; this.color = color; - this.sprite = new PIXI.Sprite(PIXI.loader.resources.nodeImg.texture); - // for debugging trainCount += 1; this.id = trainCount; - this.textStyle = { - fill: '#AEAEAE', - fontFamily: 'monospace', - fontSize: '12px', - }; - this.renderLabel(); - } - - public boardPassengers() { - if (this.location === this.origin.location) { // about to leave a station - } - } - - public renderLabel() { - this.label = new PIXI.Text(`${this.id}`, this.textStyle); } } diff --git a/src/Train.worker.ts b/src/Train.worker.ts new file mode 100644 index 0000000..79f2342 --- /dev/null +++ b/src/Train.worker.ts @@ -0,0 +1,116 @@ +import Point from 'pixi.js/lib/core/math/Point'; +import * as tinycolor from 'tinycolor2'; + +import Station from './Station'; +import Train from './Train'; +import { distance, pointsAlmostEqual, randomInt, weightedRandom } from './utils'; + +// TODO: define these in a common constants file +const MAX_SPEED = 10.0; +const ACCELERATION = 0.025; +const APPROACH_DISTANCE = 3.0; +const TRAIN_CAPACITY = 50; + +const ctx: Worker = self as any; + +let stations: Station[] = []; +let trains: Train[] = []; + +const initTrains = (numTrains: number, stations: Station[]): Train[] => { + const trains = []; + const stationsWithConnections = stations.filter(station => station.connections.length > 0); + for (let i = 0; i < numTrains; i += 1) { + const originStation = stationsWithConnections[ + Math.floor(Math.random() * stationsWithConnections.length) + ]; + trains.push(new Train( + new Point(originStation.location.x, originStation.location.y), + 0, 0, originStation, undefined, tinycolor('grey').toRgb()), + ); + } + return trains; +}; + +const moveTrains = (trains: Train[], stations: Station[]) => { + for (const train of trains) { + if (train.origin.connections.length === 0) { + // train is stuck at an orphaned station + continue; + } + // choose a destination randomly with a bias towards larger stations + if (train.destination === undefined) { + const otherStations = train.origin.connections.map(conn => conn.station); + const closeStationWeights = otherStations.map(station => station.population); + train.destination = weightedRandom(otherStations, closeStationWeights); + + // board passengers + const boardingPassengers = randomInt(0, Math.min(TRAIN_CAPACITY - train.passengers, + train.origin.population)); + // set or mix train color with the color of new passenger origin + if (train.passengers === 0) { + train.color = train.origin.color; + } else { + train.color = tinycolor.mix( + train.color, + train.origin.color, + Math.round((boardingPassengers / train.passengers) * 100), + ).toRgb(); + } + train.passengers += boardingPassengers; + train.origin.population -= boardingPassengers; + } + + // train reached destination, stop moving and let passengers off + if (pointsAlmostEqual(train.location, train.destination.location)) { + train.speed = 0; + + // average destination color with passenger color weighted by ratio + // (a simulation of culture mixing) + train.destination.color = tinycolor.mix( + train.destination.color, + train.origin.color, + Math.round((train.passengers / train.destination.population) * 100), + ).toRgb(); + + // transfer passengers to destination + const disembarkingPassengers = randomInt(0, train.passengers); + train.destination.population += disembarkingPassengers; + train.passengers -= disembarkingPassengers; + + // prepare for next journey + train.origin = train.destination; + train.destination = undefined; + continue; + } + + const journeyLeft = distance(train.location, train.destination.location); + + if ((train.speed / ACCELERATION) >= ((journeyLeft / train.speed) - APPROACH_DISTANCE) && + train.speed !== ACCELERATION) { + // slowing down + train.speed -= ACCELERATION; + } else if (train.speed < MAX_SPEED) { + // speeding up + train.speed += ACCELERATION; + } + + // advance train + const progress = train.speed / journeyLeft; + train.location.x += ((train.destination.location.x - train.location.x) * progress); + train.location.y += ((train.destination.location.y - train.location.y) * progress); + } +}; + +ctx.addEventListener('message', (event: MessageEvent) => { + if ('initTrains' in event.data) { + const { numTrains } = event.data.initTrains; + stations = event.data.initTrains.stations; + trains = initTrains(numTrains, stations); + ctx.postMessage({ initTrains: trains }); + } else if ('moveTrains' in event.data) { + // trains = event.data.moveTrains.trains; + // stations = event.data.moveTrains.stations; + moveTrains(trains, stations); + ctx.postMessage({ moveTrains: trains }); + } +}); diff --git a/src/transport.ts b/src/transport.ts index b630bba..6e44773 100644 --- a/src/transport.ts +++ b/src/transport.ts @@ -6,18 +6,20 @@ import * as tinycolor from 'tinycolor2'; import Direction from './Direction'; import Line from './Line'; import Station from './Station'; +import StationWorker from 'worker-loader!./Station.worker'; import Train from './Train'; import { distance, pointsAlmostEqual, pointsEqual, randomInt, randomPoint, rangeMap, weightedRandom } from './utils'; +import TrainWorker from 'worker-loader!./Train.worker'; import * as imgNode from './node.png'; import './style.css'; const NODE_RES = 100; -const MAX_SPEED = 10.0; -const ACCELERATION = 0.025; -const APPROACH_DISTANCE = 3.0; +const CONNECTION_RADIUS = Math.floor(Math.sqrt( + Math.pow(window.innerWidth, 2) + Math.pow(window.innerHeight, 2), +) / 8); const TRAIN_CAPACITY = 50; const LINE_CONNECTION_LIMIT = 5; const WORLD_WIDTH = 1000; @@ -29,161 +31,49 @@ const ZOOM_MAX_HEIGHT = 4000; const trainTexts: PIXI.Text[] = []; -const initStations = (numStations: number): Station[] => { - const stations: Station[] = []; - for (let i = 0; i < numStations; i += 1) { - stations.push(new Station( - Station.randomDistantPoint(stations, 30), - randomInt(300, 2000), - tinycolor.random())); - } - return stations; -}; - -const initTrains = (numTrains: number, stations: Station[]): Train[] => { - const trains = []; - const stationsWithConnections = stations.filter(station => station.connections.length > 0); - for (let i = 0; i < numTrains; i += 1) { - const originStation = stationsWithConnections[ - Math.floor(Math.random() * stationsWithConnections.length) - ]; - trains.push(new Train( - new PIXI.Point(originStation.location.x, originStation.location.y), - 0, 0, originStation, undefined, tinycolor('grey')), - ); - } - return trains; -}; - -const initLines = (numLines: number, stations: Station[]): Line[] => { - const lines = []; - for (let i = 0; i < numLines; i += 1) { - let color = tinycolor.random(); - while (color.isDark()) { - color = tinycolor.random(); - } - const stationsWithoutConnections = stations.filter(station => - station.connections.length === 0, - ); - let centralHub: Station; - if (stationsWithoutConnections.length > 0) { - centralHub = Station.largestStation(stationsWithoutConnections); - } else { - centralHub = stations[randomInt(0, stations.length - 1)]; - } - const line = new Line(`line-${i}`, tinycolor.random()); - const stationsLeft = stations.slice(0); - line.connectStations(centralHub, stationsLeft, [], LINE_CONNECTION_LIMIT); - lines.push(line); - } - return lines; -}; - -const moveTrains = (trains: Train[], stations: Station[]) => { - for (const train of trains) { - if (train.origin.connections.length === 0) { - // train is stuck at an orphaned station - continue; - } - // choose a destination randomly with a bias towards larger stations - if (train.destination === undefined) { - const otherStations = train.origin.connections.map(conn => conn.station); - const closeStationWeights = otherStations.map(station => station.population); - train.destination = weightedRandom(otherStations, closeStationWeights); - - // board passengers - const boardingPassengers = randomInt(0, Math.min(TRAIN_CAPACITY - train.passengers, - train.origin.population)); - // set or mix train color with the color of new passenger origin - if (train.passengers === 0) { - train.color = train.origin.color; - } else { - train.color = tinycolor.mix( - train.color, - train.origin.color, - Math.round((boardingPassengers / train.passengers) * 100), - ); - } - train.passengers += boardingPassengers; - train.origin.population -= boardingPassengers; - } - - // train reached destination, stop moving and let passengers off - if (pointsAlmostEqual(train.location, train.destination.location)) { - train.speed = 0; - - // average destination color with passenger color weighted by ratio - // (a simulation of culture mixing) - train.destination.color = tinycolor.mix( - train.destination.color, - train.origin.color, - Math.round((train.passengers / train.destination.population) * 100), - ); - - // transfer passengers to destination - const disembarkingPassengers = randomInt(0, train.passengers); - train.destination.population += disembarkingPassengers; - train.passengers -= disembarkingPassengers; - - // prepare for next journey - train.origin = train.destination; - train.destination = undefined; - continue; - } - - const journeyLeft = distance(train.location, train.destination.location); - - if ((train.speed / ACCELERATION) >= ((journeyLeft / train.speed) - APPROACH_DISTANCE) && - train.speed !== ACCELERATION) { - // slowing down - train.speed -= ACCELERATION; - } else if (train.speed < MAX_SPEED) { - // speeding up - train.speed += ACCELERATION; - } - - // advance train - const progress = train.speed / journeyLeft; - train.location.x += ((train.destination.location.x - train.location.x) * progress); - train.location.y += ((train.destination.location.y - train.location.y) * progress); - } -}; - -const drawStations = (stations: Station[], graphics: PIXI.Graphics) => { - for (const station of stations) { +const drawStations = (stations: Station[], stationLabels: PIXI.Text[], graphics: PIXI.Graphics) => { + stations.forEach((station, i) => { const radius = station.population / 150; - graphics.beginFill(parseInt(station.color.toHex(), 16), 0.5); + const color = tinycolor(station.color); + graphics.beginFill(parseInt(color.toHex(), 16), 0.5); graphics.drawCircle(station.location.x, station.location.y, radius); graphics.endFill(); - station.label.x = station.location.x + radius + 1; - station.label.y = station.location.y + radius + 1; - } + stationLabels[i].x = station.location.x + radius + 1; + stationLabels[i].y = station.location.y + radius + 1; + }); }; -const drawTrains = (trains: Train[], graphics: PIXI.Graphics) => { - for (const train of trains) { +const drawTrains = ( + trains: Train[], + trainLabels: PIXI.Text[], + trainSprites: PIXI.Sprite[], + graphics: PIXI.Graphics, +) => { + trains.forEach((train, i) => { const trainSize = rangeMap(train.passengers, 0, TRAIN_CAPACITY, 1, 5); const scale = trainSize / NODE_RES; - train.sprite.x = train.location.x; - train.sprite.y = train.location.y; - train.sprite.scale.x = scale; - train.sprite.scale.y = scale; - train.sprite.tint = parseInt(train.color.toHex(), 16); - train.label.x = train.location.x + scale + 1; - train.label.y = train.location.y + scale + 1; - } + const color = tinycolor(train.color); + trainSprites[i].x = train.location.x; + trainSprites[i].y = train.location.y; + trainSprites[i].scale.x = scale; + trainSprites[i].scale.y = scale; + trainSprites[i].tint = parseInt(color.toHex(), 16); + trainLabels[i].x = train.location.x + scale + 1; + trainLabels[i].y = train.location.y + scale + 1; + }); }; const drawLines = (stations: Station[], graphics: PIXI.Graphics) => { for (const station of stations) { for (const connection of station.connections) { + const color = tinycolor(connection.line.color); let twoWay = false; for (const conn of connection.station.connections) { if (conn.station === station) { twoWay = true; } } - graphics.lineStyle(twoWay ? 2 : 1, parseInt(connection.line.color.toHex(), 16), 1); + graphics.lineStyle(twoWay ? 2 : 1, parseInt(color.toHex(), 16), 1); graphics.moveTo(station.location.x, station.location.y); graphics.lineTo(connection.station.location.x, connection.station.location.y); } @@ -210,28 +100,85 @@ const run = () => { let stations: Station[] = []; let lines: Line[] = []; - let stationsWithConnections: Station[] = []; - while (stationsWithConnections.length === 0) { - // If all stations are too far away to connect, try generating again - stations = initStations(30); - lines = initLines(4, stations); - stationsWithConnections = stations.filter(station => station.connections.length > 0); - } - const trains = initTrains(50, stations); + let trains: Train[] = []; + const stationLabels: PIXI.Text[] = []; + const trainLabels: PIXI.Text[] = []; + const trainSprites: PIXI.Sprite[] = []; + const stationWorker = new StationWorker(); + const trainWorker = new TrainWorker(); + + stationWorker.postMessage({ initStations: { + connectionRadius: CONNECTION_RADIUS, + height: window.innerHeight, + numLines: 4, + numStations: 30, + width: window.innerWidth, + }}); + stationWorker.onmessage = (event: MessageEvent) => { + stations = event.data.stations; + lines = event.data.lines; + console.log(stations); + console.log(lines); + + trainWorker.postMessage({ initTrains: { + stations, + + numTrains: 50, + }}); + trainWorker.onmessage = (trainEvent: MessageEvent) => { + if ('initTrains' in trainEvent.data) { + trains = trainEvent.data.initTrains; + + // add train sprites + for (const train of trains) { + const sprite = new PIXI.Sprite(PIXI.loader.resources.nodeImg.texture); + sprite.visible = false; + trainSprites.push(sprite); + viewport.addChild(sprite); + } + // Add debug labels + for (const train of trains) { + const label = new PIXI.Text( + `${train.id}`, { + fill: '#AEAEAE', + fontFamily: 'monospace', + fontSize: '12px', + }, + ); + trainLabels.push(label); + viewport.addChild(label); + } + } else if ('moveTrains' in trainEvent.data) { + trains = trainEvent.data.moveTrains; + } + }; + + for (const station of stations) { + const label = new PIXI.Text( + `${station.id}`, { + fill: '#FFA500', + fontFamily: 'monospace', + fontSize: '12px', + }, + ); + stationLabels.push(label); + viewport.addChild(label); + } + }; ticker.stop(); ticker.add((deltaTime) => { stats.begin(); - moveTrains(trains, stations); + trainWorker.postMessage({ moveTrains: {} }); graphics.clear(); graphics.lineStyle(1, 0xFFA500, 1); - drawStations(stations, graphics); + drawStations(stations, stationLabels, graphics); graphics.lineStyle(1, 0xAEAEAE, 1); - drawTrains(trains, graphics); + drawTrains(trains, trainLabels, trainSprites, graphics); drawLines(stations, graphics); @@ -240,17 +187,6 @@ const run = () => { ticker.start(); viewport.addChild(graphics); - // add train sprites - for (const train of trains) { - viewport.addChild(train.sprite); - } - // Add debug labels - for (const train of trains) { - viewport.addChild(train.label); - } - for (const station of stations) { - viewport.addChild(station.label); - } document.body.appendChild(app.view); app.stage.addChild(viewport); viewport.drag().pinch().wheel().clampZoom({ diff --git a/src/utils.ts b/src/utils.ts index 08be619..0431bbb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,4 +1,4 @@ -import * as PIXI from 'pixi.js'; +import Point from 'pixi.js/lib/core/math/Point'; const EPSILON = 1.0; @@ -19,20 +19,20 @@ export const weightedRandom = (choices: any[], weights: number[]): any => { } }; -export const randomPoint = () => ( - new PIXI.Point(randomInt(0, window.innerWidth), randomInt(0, window.innerHeight)) +export const randomPoint = (height: number, width: number): Point => ( + new Point(randomInt(0, width), randomInt(0, height)) ); -export const pointsEqual = (pointA: PIXI.Point, pointB: PIXI.Point): boolean => ( +export const pointsEqual = (pointA: Point, pointB: Point): boolean => ( (pointA.x === pointB.x && pointA.y === pointB.y) ); -export const pointsAlmostEqual = (pointA: PIXI.Point, pointB: PIXI.Point): boolean => ( +export const pointsAlmostEqual = (pointA: Point, pointB: Point): boolean => ( Math.abs(pointA.x - pointB.x) < EPSILON && Math.abs(pointA.y - pointB.y) < EPSILON ); -export const distance = (pointA: PIXI.Point, pointB: PIXI.Point): number => { +export const distance = (pointA: Point, pointB: Point): number => { const distX = pointA.x - pointB.x; const distY = pointA.y - pointB.y; return Math.sqrt((distX * distX) + (distY * distY)); @@ -43,10 +43,10 @@ export const rangeMap = (num: number, inMin: number, inMax: number, (num - inMin) * (outMax - outMin) / (inMax - inMin) + outMin ); -export const angleRadians = (pointA: PIXI.Point, pointB: PIXI.Point): number => ( +export const angleRadians = (pointA: Point, pointB: Point): number => ( Math.atan2(-(pointB.x - pointA.x), pointB.y - pointA.y) ); -export const angleDegrees = (pointA: PIXI.Point, pointB: PIXI.Point): number => ( +export const angleDegrees = (pointA: Point, pointB: Point): number => ( 180 + angleRadians(pointA, pointB) * (180 / Math.PI) ); diff --git a/typings/custom.d.ts b/typings/custom.d.ts index e2937d4..469205c 100644 --- a/typings/custom.d.ts +++ b/typings/custom.d.ts @@ -1 +1,17 @@ declare module '*.png'; + +declare module 'worker-loader!*' { + class WebpackWorker extends Worker { + constructor(); + } + + export default WebpackWorker; +} + +declare module 'pixi.js/lib/core/math/Point' { + export default PIXI.Point; +} + +declare module 'pixi.js/lib/core/text/Text' { + export default PIXI.Text; +}