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.
This commit is contained in:
2018-04-14 23:26:10 -04:00
parent 61642cb70f
commit 95805184ea
5 changed files with 146 additions and 21 deletions

41
src/Direction.ts Normal file
View File

@@ -0,0 +1,41 @@
import * as PIXI from 'pixi.js';
import { angleDegrees } from './utils';
enum Direction {
North, // 0
Northeast, // 1
East, // 2
Southeast, // 3
South, // 4
Southwest, // 5
West, // 6
Northwest, // 7
}
export const getPointDirection = (pointA: PIXI.Point, pointB: PIXI.Point): Direction => {
const angle = angleDegrees(pointA, pointB);
let direction = null;
if (angle >= 337.5 || angle < 22.5) {
direction = Direction.North;
} else if (angle >= 22.5 && angle < 67.5) {
direction = Direction.Northeast;
} else if (angle >= 67.5 && angle < 112.5) {
direction = Direction.East;
} else if (angle >= 112.5 && angle < 157.5) {
direction = Direction.Southeast;
} else if (angle >= 157.5 && angle < 202.5) {
direction = Direction.South;
} else if (angle >= 202.5 && angle < 247.5) {
direction = Direction.Southwest;
} else if (angle >= 247.5 && angle < 292.5) {
direction = Direction.West;
} else if (angle >= 292.5 && angle < 337.5) {
direction = Direction.Northwest;
} else {
throw Error('Angle between points is not a valid degree');
}
return direction;
};
export default Direction;

View File

@@ -1,25 +1,67 @@
import * as tinycolor from 'tinycolor2';
import Direction, { getPointDirection } from './Direction';
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),
) / 4);
export default class Line {
public static getLinesWithStation(lines: Line[], station: Station): Line[] {
return lines.filter(line => line.stations.indexOf(station) >= 0);
}
public stations: Station[];
public color: tinycolorInstance;
constructor(stations: Station[], numStations: number) {
constructor(stations: Station[], start: PIXI.Point, startDirection: Direction,
numStations: number, color: tinycolorInstance) {
this.color = color;
this.stations = [];
let stationsLeft = stations;
let largest = Station.largestStation(stationsLeft);
stationsLeft = stationsLeft.filter(s => s !== largest);
this.stations.push(largest);
let stationsLeft = stations.slice();
let currentStation = Station.randomCloseLargeStation(stationsLeft, start, CONNECTION_RADIUS);
stationsLeft = stationsLeft.filter(s => s !== currentStation);
this.stations.push(currentStation);
let direction = startDirection;
while (this.stations.length < numStations) {
largest = Station.largestStation(
Station.stationsWithinRadius(stationsLeft, largest.location, 500),
const previousStation = this.stations[this.stations.length - 1];
let candidateStations = Station.stationsWithinRadius(
stationsLeft,
currentStation.location,
CONNECTION_RADIUS,
);
if (largest === null) {
if (this.stations.length > 1) {
const secondPreviousStation = this.stations[this.stations.length - 2];
direction = getPointDirection(secondPreviousStation.location,
previousStation.location);
}
const straightStations = Station.stationsInDirection(
candidateStations, previousStation.location, direction,
);
const leftStations = Station.stationsInDirection(
candidateStations, previousStation.location, (direction - 1) % 7,
);
const rightStations = Station.stationsInDirection(
candidateStations, previousStation.location, (direction + 1) % 7,
);
candidateStations = [
...straightStations,
...leftStations,
...rightStations,
];
currentStation = Station.randomCloseLargeStation(candidateStations, previousStation.location,
CONNECTION_RADIUS);
if (currentStation === null || currentStation === undefined) {
break;
}
stationsLeft = stationsLeft.filter(s => s !== largest);
this.stations.push(largest);
stationsLeft = stationsLeft.filter(s => s !== currentStation);
this.stations.push(currentStation);
}
}
}

View File

@@ -1,6 +1,7 @@
import * as tinycolor from 'tinycolor2';
import { distance, randomPoint } from './utils';
import Direction, { getPointDirection } from './Direction';
import { distance, randomPoint, weightedRandom } from './utils';
let stationCount = 0;
@@ -21,12 +22,25 @@ export default class Station {
return stations.filter(station => distance(point, station.location) <= radius);
}
public static closestStation(stations: Station[], point: PIXI.Point, num: number): Station {
public static stationsInDirection(stations: Station[], point: PIXI.Point,
direction: Direction): Station[] {
return stations.filter(station => getPointDirection(point, station.location) === direction);
}
public static closestStation(stations: Station[], point: PIXI.Point): Station {
return stations.reduce(
(prev, curr) => distance(point, prev.location) > distance(point, curr.location) ? prev : curr,
(prev, curr) => distance(point, prev.location) < distance(point, curr.location) ? prev : curr,
);
}
public static randomCloseLargeStation(stations: Station[], point: PIXI.Point,
radius: number): Station {
const closeStations = Station.stationsWithinRadius(stations, point,
radius);
const closeStationWeights = closeStations.map(station => station.population);
return weightedRandom(closeStations, closeStationWeights);
}
public static isPointDistant(point: PIXI.Point, stations: Station[],
minDistance: number): boolean {
for (const station of stations) {

View File

@@ -1,6 +1,7 @@
import * as PIXI from 'pixi.js';
import * as tinycolor from 'tinycolor2';
import Direction from './Direction';
import Line from './Line';
import Station from './Station';
import Train from './Train';
@@ -119,11 +120,14 @@ const drawTrains = (trains: Train[], graphics: PIXI.Graphics) => {
}
};
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 drawLines = (lines: Line[], graphics: PIXI.Graphics) => {
for (const line of lines) {
graphics.lineStyle(1, parseInt(line.color.toHex(), 16), 1);
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);
}
}
};
@@ -143,7 +147,24 @@ const run = () => {
const stations = initStations(30);
const trains = initTrains(50, stations);
// const line = new Line(stations, 10);
const lines = [
new Line(
stations, new PIXI.Point(0, 0),
Direction.Southeast, 12, tinycolor('red'),
),
new Line(
stations, new PIXI.Point(window.innerWidth, 0),
Direction.Southwest, 12, tinycolor('darkcyan'),
),
new Line(
stations, new PIXI.Point(window.innerWidth, window.innerHeight),
Direction.Northwest, 12, tinycolor('yellow'),
),
new Line(
stations, new PIXI.Point(0, window.innerHeight),
Direction.Northeast, 12, tinycolor('green'),
),
];
ticker.stop();
ticker.add((deltaTime) => {
@@ -158,8 +179,7 @@ const run = () => {
graphics.lineStyle(1, 0xAEAEAE, 1);
drawTrains(trains, graphics);
// TODO: ?
// drawLine(line, graphics);
drawLines(lines, graphics);
});
ticker.start();

View File

@@ -42,3 +42,11 @@ export const rangeMap = (num: number, inMin: number, inMax: number,
outMin: number, outMax: number): number => (
(num - inMin) * (outMax - outMin) / (inMax - inMin) + outMin
);
export const angleRadians = (pointA: PIXI.Point, pointB: PIXI.Point): number => (
Math.atan2(-(pointB.x - pointA.x), pointB.y - pointA.y)
);
export const angleDegrees = (pointA: PIXI.Point, pointB: PIXI.Point): number => (
180 + angleRadians(pointA, pointB) * (180 / Math.PI)
);