Files
dv-community-map/js/mapdata.js
T

586 lines
26 KiB
JavaScript

'use strict';
import * as Vector from './vectorlib.js';
import * as Bezier from './bezierlib.js';
import * as Utils from './utillib.js';
import {Color} from './colorlib.js';
import * as Config from './../config.js';
export let railTracks = [];
export let markers = [];
let visibleMarkers = [];
let uniqueTrackNames = {};
export let testPoint = {x:0,y:0};
export let matrix = {
initialize: () => {
matrix.minX = Infinity;
matrix.minY = Infinity;
matrix.minAlt = Infinity;
matrix.maxX = -Infinity;
matrix.maxY = -Infinity;
matrix.maxAlt = -Infinity;
for(const railTrack of railTracks){
for(const curve of railTrack.curves){
matrix.minX = Math.min(matrix.minX, curve.start.x, curve.h1.x, curve.h2.x, curve.end.x);
matrix.minY = Math.min(matrix.minY, curve.start.z, curve.h1.z, curve.h2.z, curve.end.z);
matrix.minAlt = Math.min(matrix.minAlt, curve.start.y, curve.h1.y, curve.h2.y, curve.end.y);
matrix.maxX = Math.max(matrix.maxX, curve.start.x, curve.h1.x, curve.h2.x, curve.end.x);
matrix.maxY = Math.max(matrix.maxY, curve.start.z, curve.h1.z, curve.h2.z, curve.end.z);
matrix.maxAlt = Math.max(matrix.maxAlt, curve.start.y, curve.h1.y, curve.h2.y, curve.end.y);
}
}
matrix.width = matrix.maxX-matrix.minX;
matrix.height = matrix.maxY-matrix.minY;
console.log(`(${matrix.minX}, ${matrix.minY}) to (${matrix.maxX}, ${matrix.maxY}). Altitude from ${matrix.minAlt} to ${matrix.maxAlt}`);
}
};
export let view = {
initialize: () => {
const mapContainer = document.getElementById('mapContainer');
let isLandscape = mapContainer.clientWidth > mapContainer.clientHeight;
view.scale = Math.min(mapContainer.clientWidth/(matrix.width + Config.defaultMapPadding*2), mapContainer.clientHeight/(matrix.height + Config.defaultMapPadding*2));
if(isLandscape){
view.x = mapContainer.clientWidth*0.6 - (matrix.width * 0.5 + matrix.minX)*view.scale;
view.y = (matrix.minY - Config.defaultMapPadding) * view.scale;
}else{
view.x = -(matrix.minX - Config.defaultMapPadding) * view.scale;
view.y = -(mapContainer.clientHeight*0.5 - (matrix.height * 0.5 + matrix.minY)*view.scale);
}
view.dirty = false;
view.convertX = x => {
return x * view.scale + view.x;
}
view.convertY = y => {
return (matrix.minY+matrix.maxY-y) * view.scale - view.y;
}
view.unconvertX = x => {
return (x - view.x) / view.scale;
}
view.unconvertY = y => {
return -((y + view.y) / view.scale - (matrix.minY + matrix.maxY));
}
}
}
export let layers = {};
export function reset(){
railTracks = [];
markers = [];
}
export function sortTracks(){
railTracks.sort((a,b) => {
if(a.curves && b.curves)
return a.curves[0].start.y-b.curves[b.curves.length-1].end.y;
return 0;
});
}
export function sortMarkers(){
markers.sort((a,b) => {
if(a.type == b.type){
switch(a.type){
case 'speed':
return b.value-a.value;
default:
return 0;
}
}
if(a.type == 'station') return 1;
if(b.type == 'station') return -1;
if(a.type == 'speed') return 1;
if(b.type == 'speed') return -1;
if(a.type == 'junction') return 1;
if(b.type == 'junction') return -1;
if(a.type == 'service') return 1;
if(b.type == 'service') return -1;
if(a.type == 'demonstratorSpawnHint') return -1;
if(b.type == 'demonstratorSpawnHint') return 1;
return 0;
});
}
export function filterToTrack(trackName, trackNum = 0){
if(typeof(trackName) == 'string'){
for(let railTrack of railTracks){
if(railTrack.name == trackName && railTrack.number == trackNum){
railTracks = [railTrack];
break;
}
}
}else if(typeof(trackName) == 'object'){
let newRailTracks = [];
for(let railTrack of railTracks){
for(let filter of trackName){
if(railTrack.name == filter.name && railTrack.number == (filter.number ?? 0)){
newRailTracks.push(railTrack);
}
}
}
railTracks = newRailTracks;
}
}
export function addTrack(trackData){
let newTrack = {
name: trackData.name,
curves: [],
bounds: {min:{x:Infinity,y:Infinity,z:Infinity},max:{x:-Infinity,y:-Infinity,z:-Infinity}},
};
if(uniqueTrackNames[trackData.name] == null){
uniqueTrackNames[trackData.name] = 0;
}else{
uniqueTrackNames[trackData.name]++;
}
newTrack.number = uniqueTrackNames[trackData.name];
let yardData = /\[(#)\]|\[(Y)\][_-]\[(.*?)\][_-]\[((.*?)[_-](.*?)[_-](.*?)|.*)\]/.exec(trackData.name);
if(yardData){
if(yardData[1]){ // track is [#], thus in yard limits, but not a specific designated yard.
newTrack.isYard = true;
}else if(yardData[2]){ // track is [Y], and is a specific yard.
newTrack.isYard = true;
newTrack.yardStation = yardData[3]; // Which station the track belongs to
newTrack.yardDesignation = yardData[4]; // Full yard designation, used on signs
newTrack.yardName = yardData[5]; // A yard, B yard, C yard, etc.
newTrack.yardSidingNumber = yardData[6]; // Which siding within the yard
newTrack.yardUsage = yardData[7]; // Outbound? Parking? Inbound? Loading? That's this.
}
}else if(/Turntable Track/.test(newTrack.name)){
newTrack.isTurntable = true;
}else if(/\[track (diverging|through)\]/.test(newTrack.name)){
newTrack.isJunction = true;
}
newTrack.randomColor = Color.random();
for(let i=0; i+1<trackData.points.length; i++){
const curveStart = trackData.points[i].position;
const curveHandle1 = trackData.points[i].h2;
const curveHandle2 = trackData.points[i+1].h1;
const curveEnd = trackData.points[i+1].position;
newTrack.curves[i] = {
start: curveStart,
h1: curveHandle1,
h2: curveHandle2,
end: curveEnd,
grade: Bezier.estimateGrade(curveStart, curveHandle1, curveHandle2, curveEnd, Config.bezierGradeResolution),
length: Bezier.estimateLength(curveStart, curveHandle1, curveHandle2, curveEnd, Config.bezierLengthResolution),
curvature: Bezier.estimateCurvature(curveStart, curveHandle1, curveHandle2, curveEnd, Config.bezierCurvatureResolution),
randomColor: Color.random(),
color: '#fff',
averageHeight: (curveStart.y+curveEnd.y) * 0.5,
bounds: Bezier.getBounds(curveStart, curveHandle1, curveHandle2, curveEnd),
}
newTrack.curves[i].gradeClass = Utils.gradeToClass(newTrack.curves[i].grade);
newTrack.curves[i].postedSpeed = Utils.radiusToSpeed(1/newTrack.curves[i].curvature);
if(newTrack.isYard || newTrack.isTurntable) newTrack.curves[i].postedSpeed = Math.min(newTrack.curves[i].postedSpeed, 50);
newTrack.curves[i].tooltip = [
`<h2>Rail</h2>"${trackData.name}"${uniqueTrackNames[trackData.name] > 0 ? ' #'+uniqueTrackNames[trackData.name] : ''} Sec${i}`,
`Max Grade: ${Math.round(newTrack.curves[i].grade*1000)/10}%`,
`Min Radius: ${Math.round(1/newTrack.curves[i].curvature)} meters`,
`Top Speed: ${Utils.radiusToSpeed(1/newTrack.curves[i].curvature)} km/h`,
`Length: ${Math.round(newTrack.curves[i].length*10)/10} meters`,
`Altitude (ASL): ${Math.round(newTrack.curves[i].averageHeight)-110} meters`,
].join('\n');
newTrack.bounds.min.x = Math.min(newTrack.bounds.min.x, newTrack.curves[i].bounds.min.x);
newTrack.bounds.max.x = Math.max(newTrack.bounds.max.x, newTrack.curves[i].bounds.max.x);
newTrack.bounds.min.y = Math.min(newTrack.bounds.min.y, newTrack.curves[i].bounds.min.y);
newTrack.bounds.max.y = Math.max(newTrack.bounds.max.y, newTrack.curves[i].bounds.max.y);
newTrack.bounds.min.z = Math.min(newTrack.bounds.min.z, newTrack.curves[i].bounds.min.z);
newTrack.bounds.max.z = Math.max(newTrack.bounds.max.z, newTrack.curves[i].bounds.max.z);
}
railTracks.push(newTrack);
}
export function addJunction(junctionData){
if(junctionData.name.startsWith('S-') || junctionData.excludeFromJunctionMap)
junctionData.minZoom = 1;
else if(junctionData.name.startsWith('W-'))
junctionData.minZoom = 0.05;
junctionData.tooltip = `<h2>Junction</h2>"${junctionData.name}" - #${junctionData.index}\n${junctionData.excludeFromJunctionMap ? 'Inv' : 'V'}isible On Map\nLinked Junctions:\n- ${junctionData.linkedJunctions.join('\n- ')}`;
junctionData.tooltipHitzone = Config.tooltipHitzone.junction;
markers.push(junctionData);
}
export function addPoi(poiData, level = 0){
let newPoi = {
type: poiData.type,
name: poiData.name,
color: poiData.color,
position: poiData.position,
};
if(level > 0) newPoi.minZoom = 0.3;
switch(poiData.type){
case 'service':
newPoi.serviceTypes = [];
if(poiData.repair) newPoi.serviceTypes.push('Repair');
if(poiData.diesel) newPoi.serviceTypes.push('Diesel');
if(poiData.charger) newPoi.serviceTypes.push('Charger');
if(newPoi.serviceTypes.length == 0){
newPoi.tooltip = '<h1>Service</h1><hr>?';
}else{
newPoi.tooltip = '<h1>Service</h1><hr>- '+newPoi.serviceTypes.join('\n- ');
}
break;
case 'station':
newPoi.maxZoom = 1;
newPoi.nickname = poiData.nickname;
newPoi.shorthand = poiData.shorthand;
newPoi.color = poiData.color;
newPoi.tooltip = `<h1>${poiData.name}</h1>`;
break;
case 'shop':
newPoi.tooltip = `<h1>Shop</h1>${poiData.stock ? '<hr>'+poiData.stock.sort().join('\n') : ''}`;
break;
case 'water':
newPoi.tooltip = '<h1>Water Tower</h1>';
break;
case 'coal':
newPoi.tooltip = '<h1>Coal Tower</h1>';
break;
case 'office':
if(poiData.museum) newPoi.tooltip = '<h1>Museum Office</h1>';
else if(poiData.military) newPoi.tooltip = '<h1>Military Office</h1>';
else newPoi.tooltip = '<h1>Office</h1>';
break;
case 'demonstratorSpawn':
newPoi.name = newPoi.name.replace('spawn anchor ', '');
newPoi.tooltip = `<h2>Demonstrator Spawnpoint</h2>${newPoi.name ?? ''}`;
break;
case 'demonstratorSpawnHint':
newPoi.tooltip = `<h2>Demonstrator Spawn Hint</h2>${newPoi.name ?? ''}`;
newPoi.radius = poiData.radius;
break;
default:
newPoi.tooltip = `<h1>${newPoi.name}</h1>${poiData.description ? '<hr>'+poiData.description : ''}`;
newPoi.minZoom = 0.07;
break;
}
if(poiData.type != 'dummy')
markers.push(newPoi);
if(poiData.children){
for(let child of poiData.children){
addPoi(child, level+(poiData.type == 'dummy' ? 0 : 1));
}
}
}
export function generateTrackSignage(){
let yardMarkers = {};
for(const railTrack of railTracks){
let trackZoneData = {};
let lastCurve = false;
let resetTzdGrade = (i=0) => {
trackZoneData.gradeClass = railTrack.curves[i].gradeClass;
trackZoneData.gradeDirection = Math.sign(railTrack.curves[i].grade);
trackZoneData.gradeZoneStart = i;
trackZoneData.gradeZoneLength = 0;
};
let resetTzdSpeed = (i=0) => {
trackZoneData.postedSpeed = railTrack.curves[i].postedSpeed;
trackZoneData.speedZoneStart = i;
trackZoneData.speedZoneLength = 0;
};
resetTzdGrade();
resetTzdSpeed();
for(let i=0; i<railTrack.curves.length; i++){
let nextIndex = i+1;
if(nextIndex == railTrack.curves.length){
lastCurve = true;
nextIndex = i;
}
//Grade Markers
trackZoneData.gradeZoneLength += railTrack.curves[i].length;
if(railTrack.curves[nextIndex].gradeClass != trackZoneData.gradeClass
|| Math.sign(railTrack.curves[nextIndex].grade) != trackZoneData.gradeDirection
|| trackZoneData.gradeZoneLength > Config.maxZoneLength
|| lastCurve){
if(railTrack.curves[i].gradeClass != 'flat'){
let markerIndex = Math.ceil((trackZoneData.gradeZoneStart + i)/2);
let markerOffset = (trackZoneData.gradeZoneStart + i) % 2 ? 0.1 : 0.6;
let b1 = railTrack.curves[markerIndex].start;
let b2 = railTrack.curves[markerIndex].h1;
let b3 = railTrack.curves[markerIndex].h2;
let b4 = railTrack.curves[markerIndex].end;
let newMarker = {
type: 'grade',
value: railTrack.curves[markerIndex].grade,
position: Bezier.evaluatePoint(b1, b2, b3, b4, markerOffset),
gradeClass: railTrack.curves[markerIndex].gradeClass,
minZoom: Math.max(0.02, Math.min(1.0, 20/trackZoneData.gradeZoneLength)),
zoneLength: trackZoneData.gradeZoneLength,
zoneCount: i - trackZoneData.gradeZoneStart + 1,
tooltipHitzone: {radius:16},
}
let tangent = newMarker.value > 0
? Bezier.evaluateVelocity(b1, b2, b3, b4, markerOffset)
: Bezier.evaluateVelocity(b4, b3, b2, b1, markerOffset);
let tangentLength = Math.sqrt(tangent.x * tangent.x + tangent.z * tangent.z) * Math.sign(tangent.y);
newMarker.rotation = Math.atan2(tangent.x/tangentLength, tangent.z/tangentLength);
newMarker.tooltip = `<h2>Grade</h2>${Math.round(newMarker.value*1000)/10}% Grade\nUphill bearing ${Utils.mod(newMarker.rotation/(Math.PI*2)*360, 360).toFixed(0)}, ${Utils.angleToCardinalDirection(newMarker.rotation)}\nSection Length: ${newMarker.zoneLength.toFixed(1)} meters\nCovers ${newMarker.zoneCount} track sections`;
markers.push(newMarker);
}
resetTzdGrade(nextIndex);
}
//Speed Markers
trackZoneData.speedZoneLength += railTrack.curves[i].length;
if(railTrack.curves[nextIndex].postedSpeed != trackZoneData.postedSpeed
|| trackZoneData.speedZoneLength > Config.maxZoneLength
|| lastCurve){
let markerIndex = Math.ceil((trackZoneData.speedZoneStart + i)/2);
let markerOffset = (trackZoneData.speedZoneStart + i) % 2 ? 0.2 : 0.7;
let b1 = railTrack.curves[markerIndex].start;
let b2 = railTrack.curves[markerIndex].h1;
let b3 = railTrack.curves[markerIndex].h2;
let b4 = railTrack.curves[markerIndex].end;
let newMarker = {
type: 'speed',
value: railTrack.curves[markerIndex].postedSpeed,
position: Bezier.evaluatePoint(b1, b2, b3, b4, markerOffset),
zoneLength: trackZoneData.speedZoneLength,
zoneCount: i - trackZoneData.speedZoneStart + 1,
minZoom: 0.05,
}
if((railTrack.isYard || railTrack.isJunction) && newMarker.value > 30){
if(newMarker.value > 40) newMarker.minZoom = 5;
else newMarker.minZoom = 0.5;
}
if(newMarker.value > 80) newMarker.minZoom *= 6.5;
else if(newMarker.value > 60) newMarker.minZoom *= 5;
else if(newMarker.value > 40) newMarker.minZoom *= 3;
if(newMarker.zoneLength > 500) newMarker.minZoom *= 0.15;
if(newMarker.zoneLength < 35 && newMarker.value > 40) newMarker.minZoom *= 6;
newMarker.tooltip = `<h2>Speed</h2>${Math.round(newMarker.value)} km/h\nSection Length: ${newMarker.zoneLength.toFixed(1)} meters\nCovers ${newMarker.zoneCount} track sections`;
markers.push(newMarker);
resetTzdSpeed(nextIndex);
}
}
if(railTrack.yardDesignation && railTrack.yardDesignation != '--'){
if(!yardMarkers[railTrack.yardStation]) yardMarkers[railTrack.yardStation] = {};
let centerIndex = Math.floor(railTrack.curves.length/2);
let yardSignPos = railTrack.curves[centerIndex].start;
if(railTrack.curves.length % 2 == 1){
let curve = railTrack.curves[centerIndex];
yardSignPos = Bezier.evaluatePoint(curve.start, curve.h1, curve.h2, curve.end, 0.5);
}
if(railTrack.yardName){
if(!yardMarkers[railTrack.yardStation][railTrack.yardName])
yardMarkers[railTrack.yardStation][railTrack.yardName] = {accCenter:{x:0,y:0,z:0},accCount:0};
yardMarkers[railTrack.yardStation][railTrack.yardName].accCenter.x += yardSignPos.x;
yardMarkers[railTrack.yardStation][railTrack.yardName].accCenter.y += yardSignPos.y;
yardMarkers[railTrack.yardStation][railTrack.yardName].accCenter.z += yardSignPos.z;
yardMarkers[railTrack.yardStation][railTrack.yardName].accCount++;
}
let newMarker = {
type: 'yardSiding',
name: railTrack.yardDesignation,
position: yardSignPos,
minZoom: 2.5,
};
if(railTrack.yardName && railTrack.yardSidingNumber && railTrack.yardUsage){
if(!Config.sidingUsageMeanings[railTrack.yardUsage]) console.log(`Unknown siding usage for ${railTrack.yardDesignation} at ${railTrack.yardStation}`);
newMarker.tooltip = `<h2>Siding</h2>${railTrack.yardDesignation}\n${Config.sidingUsageMeanings[railTrack.yardUsage]}`;
}else{
newMarker.tooltip = `<h2>Siding</h2>${railTrack.yardDesignation}`;
}
markers.push(newMarker);
}
}
for(let station in yardMarkers){
for(let yard in yardMarkers[station]){
markers.push({
type: 'yard',
name: `${yard} Yard`,
position: {
x: yardMarkers[station][yard].accCenter.x/yardMarkers[station][yard].accCount,
y: yardMarkers[station][yard].accCenter.y/yardMarkers[station][yard].accCount,
z: yardMarkers[station][yard].accCenter.z/yardMarkers[station][yard].accCount,
},
tooltip: `<h1>${yard} Yard</h1>`,
maxZoom: 2.5,
minZoom: 0.25,
});
}
}
}
/**
* Finds the relevant tooltip for where the cursor is (if possible)
* Returns "" if there is none.
* @param {Number} cursorX
* @param {Number} cursorY
* @returns {String}
*/
export function testTooltip(cursorX, cursorY){
cursorX = view.unconvertX(cursorX);
cursorY = view.unconvertY(cursorY);
let finalTooltip = '';
let radius = 0;
let width = 0;
let height = 0;
for(const marker of markers){
if(!marker.visible) continue;
if(marker.boundsCheck != null){
if(marker.boundsCheck(cursorX, cursorY))
finalTooltip = marker.tooltip ?? finalTooltip;
}else if(marker.tooltipHitzone && marker.tooltipHitzone.width && marker.tooltipHitzone.height){
width = marker.tooltipHitzone.width * 0.5/view.scale;
height = marker.tooltipHitzone.height * 0.5/view.scale;
if( marker.position.x - cursorX + (marker.tooltipHitzone.offsetX ?? 0)/view.scale <= width
&& marker.position.x - cursorX + (marker.tooltipHitzone.offsetX ?? 0)/view.scale >= -width
&& marker.position.z - cursorY + (marker.tooltipHitzone.offsetY ?? 0)/view.scale <= height
&& marker.position.z - cursorY + (marker.tooltipHitzone.offsetY ?? 0)/view.scale >= -height){
finalTooltip = marker.tooltip ?? finalTooltip;
}
}else{
if(marker.tooltipHitzone)
radius = (marker.tooltipHitzone.radius ?? Config.tooltipHitzone.default.radius) / view.scale;
else
radius = Config.tooltipHitzone.default.radius / view.scale;
if(Math.pow(marker.position.x - cursorX, 2) + Math.pow(marker.position.z - cursorY, 2) <= radius * radius){
finalTooltip = marker.tooltip ?? finalTooltip;
}
}
}
if(finalTooltip != '') return finalTooltip;
let bestDistance = Infinity;
let test = {};
let boundsPadding = 40/view.scale;
for(const railTrack of railTracks){
if(cursorX >= railTrack.bounds.min.x - boundsPadding && cursorX <= railTrack.bounds.max.x + boundsPadding && cursorY >= railTrack.bounds.min.z - boundsPadding && cursorY <= railTrack.bounds.max.z + boundsPadding){
for(const curve of railTrack.curves){
if(cursorX >= curve.bounds.min.x - boundsPadding && cursorX <= curve.bounds.max.x + boundsPadding && cursorY >= curve.bounds.min.z - boundsPadding && cursorY <= curve.bounds.max.z + boundsPadding){
test = Bezier.getNearestPoint2D(curve.start, curve.h1, curve.h2, curve.end, {x:cursorX, y:cursorY}, Math.floor(curve.length*2));
if(test.distance < bestDistance){
bestDistance = test.distance;
finalTooltip = curve.tooltip;
testPoint = test.point;
}
}
}
}
}
return bestDistance <= Math.min(Math.max(0.9, 12/view.scale), 120) ? finalTooltip : '';
}
export function setTrackColorMode(mode){
let modeFunction;
const keyElement = document.getElementById('legendKeyColors');
let gradientString = '';
keyElement.innerHTML = '';
switch(mode){
case 'Track Type':
modeFunction = (curve, railTrack) => {
let finalColor = '#aaa';
if(railTrack.isYard && railTrack.yardDesignation) finalColor = '#4bf';
else if(railTrack.isYard) finalColor = '#4fb';
else if(railTrack.isJunction) finalColor = '#fb4';
else if(railTrack.isTurntable) finalColor = '#b4f';
curve.color = finalColor;
};
keyElement.innerHTML = `
<span class="colorKey"><span style="background:#4bf"></span> Designated Yard</span>
<span class="colorKey"><span style="background:#4fb"></span> Other Yard</span>
<span class="colorKey"><span style="background:#fb4"></span> Point</span>
<span class="colorKey"><span style="background:#b4f"></span> Turntable</span>
<span class="colorKey"><span style="background:#aaa"></span> Other Tracks</span>`;
break;
case 'Grade':
modeFunction = curve => {
curve.color = Config.gradeColors['grade_'+curve.gradeClass];
};
for(let grade in Config.gradeColors){
keyElement.innerHTML += `<span class="colorKey" style="${grade.replace('grade_','') < 6 || grade == 'grade_flat' ? 'color:#000;' : ''}padding:0px 3px;background:${Config.gradeColors[grade]}">${(Utils.classToGrade(grade.replace('grade_',''))*100).toFixed(1)}%</span>`;
}
break;
case 'Altitude':
modeFunction = (curve, railTrack) => {
curve.color = Color.blendGradient(Config.altitudeGradient, (curve.averageHeight-matrix.minAlt)/(matrix.maxAlt-matrix.minAlt)).hex;
}
gradientString = 'linear-gradient(to right';
for(let col of Config.altitudeGradient) gradientString += ', '+col.hex;
gradientString += ')';
keyElement.innerHTML = `
<div style="display:flex">
<div>${Math.floor(matrix.minAlt)}m</div>
<div class="gradientKey" style="flex-grow:1;background:${gradientString}"></div>
<div>${Math.ceil(matrix.maxAlt)}m</div>
</div>
`;
break;
case 'Speed':
modeFunction = curve => {
curve.color = Color.blendGradient(Config.speedGradient, (curve.postedSpeed+10)/110).hex;
};
gradientString = 'linear-gradient(to right';
for(let col of Config.speedGradient) gradientString += ', '+col.hex;
gradientString += ')';
keyElement.innerHTML = `
<div style="display:flex">
<div>10 kmh</div>
<div class="gradientKey" style="flex-grow:1;background:${gradientString}"></div>
<div>120 kmh</div>
</div>
`;
break;
case 'Track Random':
modeFunction = (curve, railTrack) => {
curve.color = railTrack.randomColor.hex;
};
keyElement.innerHTML = 'Random per continuous track section';
break;
case 'Curve Random':
modeFunction = curve => {
curve.color = curve.randomColor.hex;
};
keyElement.innerHTML = 'Random per individual track bezier curve';
break;
default:
modeFunction = curve => {
curve.color = '#fff';
};
break;
}
if(modeFunction != null){
for(let railTrack of railTracks){
for(let curve of railTrack.curves){
modeFunction(curve, railTrack);
}
}
}
view.dirty = true;
}