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:
41
src/Direction.ts
Normal file
41
src/Direction.ts
Normal 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;
|
||||
62
src/Line.ts
62
src/Line.ts
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user