Add Track Colorization
This commit is contained in:
1 parent
403ee48757
commit
bf72b68d8a
6 files changed
+157
-27
No files matched your search
@@ -1,4 +1,5 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
import {Color} from './js/colorlib.js';
|
||||||
|
|
||||||
export const gradeColors = {
|
export const gradeColors = {
|
||||||
grade_flat: '#fff',
|
grade_flat: '#fff',
|
||||||
@@ -13,3 +14,18 @@ export const gradeColors = {
|
|||||||
grade_8: '#a3008d',
|
grade_8: '#a3008d',
|
||||||
grade_9: '#690097',
|
grade_9: '#690097',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const speedGradient = [
|
||||||
|
new Color('#370065'),
|
||||||
|
new Color('#79146e'),
|
||||||
|
new Color('#d53354'),
|
||||||
|
new Color('#fa8256'),
|
||||||
|
new Color('#fbffb2'),
|
||||||
|
]
|
||||||
|
|
||||||
|
export const altitudeGradient = [
|
||||||
|
new Color('#340042'),
|
||||||
|
new Color('#235f7b'),
|
||||||
|
new Color('#51c34e'),
|
||||||
|
new Color('#fce51e'),
|
||||||
|
]
|
||||||
@@ -239,6 +239,9 @@
|
|||||||
<h3>Key</h3>
|
<h3>Key</h3>
|
||||||
<hr>
|
<hr>
|
||||||
<div id="legendKey"></div>
|
<div id="legendKey"></div>
|
||||||
|
<hr>
|
||||||
|
Track Colors:
|
||||||
|
<div id="legendKeyColors"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="settingsPanel" class="panel">
|
<div id="settingsPanel" class="panel">
|
||||||
<h3>Map Settings</h3>
|
<h3>Map Settings</h3>
|
||||||
|
|||||||
@@ -288,6 +288,8 @@ function establishDimensions(){
|
|||||||
x2: maxX,
|
x2: maxX,
|
||||||
y1: minY,
|
y1: minY,
|
||||||
y2: maxY,
|
y2: maxY,
|
||||||
|
minAlt: minAlt,
|
||||||
|
maxAlt: maxAlt,
|
||||||
width: width,
|
width: width,
|
||||||
height: height,
|
height: height,
|
||||||
fixY: yIn => {
|
fixY: yIn => {
|
||||||
@@ -503,8 +505,11 @@ function sortTracks(){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTrackColorMode(mode){
|
export function setTrackColorMode(mode){
|
||||||
let modeFunction = null;
|
let modeFunction;
|
||||||
|
const keyElement = document.getElementById('legendKeyColors');
|
||||||
|
let gradientString = '';
|
||||||
|
keyElement.innerHTML = '';
|
||||||
switch(mode){
|
switch(mode){
|
||||||
case 'Track Type':
|
case 'Track Type':
|
||||||
modeFunction = (section, curve) => {
|
modeFunction = (section, curve) => {
|
||||||
@@ -515,30 +520,73 @@ function setTrackColorMode(mode){
|
|||||||
else if(curve.isTurntable) finalColor = '#b4f';
|
else if(curve.isTurntable) finalColor = '#b4f';
|
||||||
|
|
||||||
section.element.setAttribute('stroke', finalColor);
|
section.element.setAttribute('stroke', 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;
|
break;
|
||||||
case 'Grade':
|
case 'Grade':
|
||||||
modeFunction = section => {
|
modeFunction = section => {
|
||||||
section.element.setAttribute('stroke', Config.gradeColors['grade_'+section.gradeClass]);
|
section.element.setAttribute('stroke', Config.gradeColors['grade_'+section.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 = section => {
|
||||||
|
section.element.setAttribute('stroke', Color.blendGradient(Config.altitudeGradient, (section.position.y-MapMatrix.minAlt)/(MapMatrix.maxAlt-MapMatrix.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(MapMatrix.minAlt)}m</div>
|
||||||
|
<div class="gradientKey" style="flex-grow:1;background:${gradientString}"></div>
|
||||||
|
<div>${Math.ceil(MapMatrix.maxAlt)}m</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
break;
|
||||||
|
case 'Speed':
|
||||||
|
modeFunction = section => {
|
||||||
|
section.element.setAttribute('stroke', Color.blendGradient(Config.speedGradient, (section.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;
|
break;
|
||||||
case 'Track Random':
|
case 'Track Random':
|
||||||
modeFunction = (section, curve) => {
|
modeFunction = (section, curve) => {
|
||||||
section.element.setAttribute('stroke', curve.randomColor);
|
section.element.setAttribute('stroke', curve.randomColor.hex);
|
||||||
}
|
};
|
||||||
|
keyElement.innerHTML = 'Random per continuous track section';
|
||||||
break;
|
break;
|
||||||
case 'Section Random':
|
case 'Curve Random':
|
||||||
modeFunction = section => {
|
modeFunction = section => {
|
||||||
section.element.setAttribute('stroke', section.randomColor);
|
section.element.setAttribute('stroke', section.randomColor.hex);
|
||||||
}
|
};
|
||||||
|
keyElement.innerHTML = 'Random per individual track bezier curve';
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
modeFunction = section => {
|
modeFunction = section => {
|
||||||
section.element.setAttribute('stroke', '#fff');
|
section.element.setAttribute('stroke', '#fff');
|
||||||
}
|
};
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(modeFunction !== null){
|
if(modeFunction != null){
|
||||||
for(let curve of mapData){
|
for(let curve of mapData){
|
||||||
for(let section of curve.points){
|
for(let section of curve.points){
|
||||||
if(!section.element) continue;
|
if(!section.element) continue;
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ export function gradeToClass(grade){
|
|||||||
return 'flat';
|
return 'flat';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function classToGrade(className){
|
||||||
|
let classNum = Math.min(Math.max(Number(className), 0), gradeIncrements.length-1);
|
||||||
|
if(Number.isNaN(classNum)) return 0;
|
||||||
|
return gradeIncrements[gradeIncrements.length-1-classNum];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Outputs an easy to read time interval between `date1` and `date2`
|
* Outputs an easy to read time interval between `date1` and `date2`
|
||||||
* @param {Date} date
|
* @param {Date} date
|
||||||
|
|||||||
@@ -2,8 +2,27 @@
|
|||||||
|
|
||||||
import * as Utils from './js/utillib.js';
|
import * as Utils from './js/utillib.js';
|
||||||
import * as Config from './config.js';
|
import * as Config from './config.js';
|
||||||
|
import { Color } from './js/colorlib.js';
|
||||||
|
import { setTrackColorMode } from './index.js';
|
||||||
|
|
||||||
let settingEntries = [
|
let settingEntries = [
|
||||||
|
{
|
||||||
|
label: 'Color Mode',
|
||||||
|
id: 'dropdown_trackColor',
|
||||||
|
options: [
|
||||||
|
'None',
|
||||||
|
'Grade',
|
||||||
|
'Altitude',
|
||||||
|
'Speed',
|
||||||
|
'Track Type',
|
||||||
|
'Track Random',
|
||||||
|
'Curve Random',
|
||||||
|
],
|
||||||
|
default: 'Grade',
|
||||||
|
func: state =>{
|
||||||
|
setTrackColorMode(state);
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: 'Signage',
|
label: 'Signage',
|
||||||
id: 'toggle_signage',
|
id: 'toggle_signage',
|
||||||
@@ -56,15 +75,6 @@ let settingEntries = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Track Color',
|
|
||||||
id: 'radio_trackColor',
|
|
||||||
options: [
|
|
||||||
'None',
|
|
||||||
'Grade',
|
|
||||||
],
|
|
||||||
default: 1,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const legendKey = document.getElementById('legendKey');
|
const legendKey = document.getElementById('legendKey');
|
||||||
@@ -93,21 +103,15 @@ document.addEventListener('DOMContentLoaded', async () => {
|
|||||||
|
|
||||||
function addSettingEntry(thisSetting, parent, indent=0){
|
function addSettingEntry(thisSetting, parent, indent=0){
|
||||||
thisSetting.divContainer = document.createElement('div');
|
thisSetting.divContainer = document.createElement('div');
|
||||||
thisSetting.inputElement = document.createElement('input');
|
|
||||||
thisSetting.inputElement.style.margin = `0 0.5em 0 ${indent}em`;
|
|
||||||
thisSetting.labelElement = document.createElement('label');
|
thisSetting.labelElement = document.createElement('label');
|
||||||
parent.appendChild(thisSetting.divContainer);
|
|
||||||
thisSetting.divContainer.appendChild(thisSetting.inputElement);
|
|
||||||
thisSetting.divContainer.appendChild(thisSetting.labelElement);
|
thisSetting.divContainer.appendChild(thisSetting.labelElement);
|
||||||
|
|
||||||
let settingType = thisSetting.id.split('_')[0];
|
let settingType = thisSetting.id.split('_')[0];
|
||||||
switch(settingType){
|
switch(settingType){
|
||||||
case 'toggle':
|
case 'toggle':
|
||||||
|
thisSetting.inputElement = document.createElement('input');
|
||||||
thisSetting.inputElement.type = 'checkbox';
|
thisSetting.inputElement.type = 'checkbox';
|
||||||
thisSetting.inputElement.id = thisSetting.id;
|
|
||||||
thisSetting.inputElement.checked = thisSetting.state;
|
thisSetting.inputElement.checked = thisSetting.state;
|
||||||
thisSetting.labelElement.innerHTML = thisSetting.label;
|
|
||||||
thisSetting.labelElement.htmlFor = thisSetting.id;
|
|
||||||
|
|
||||||
if(thisSetting.func){
|
if(thisSetting.func){
|
||||||
thisSetting.inputElement.addEventListener('input', e =>{
|
thisSetting.inputElement.addEventListener('input', e =>{
|
||||||
@@ -120,10 +124,50 @@ function addSettingEntry(thisSetting, parent, indent=0){
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'dropdown':
|
||||||
|
thisSetting.inputElement = document.createElement('select');
|
||||||
|
for(let inputEntry of thisSetting.options){
|
||||||
|
let optionElement = document.createElement('option');
|
||||||
|
if(typeof(inputEntry) == 'object'){
|
||||||
|
optionElement.value = inputEntry[0];
|
||||||
|
optionElement.innerHTML = inputEntry[1];
|
||||||
|
}else{
|
||||||
|
optionElement.value = inputEntry;
|
||||||
|
optionElement.innerHTML = inputEntry;
|
||||||
|
}
|
||||||
|
if(inputEntry == thisSetting.default) optionElement.selected = true;
|
||||||
|
thisSetting.inputElement.appendChild(optionElement);
|
||||||
|
}
|
||||||
|
if(thisSetting.func){
|
||||||
|
thisSetting.inputElement.addEventListener('change', e =>{
|
||||||
|
thisSetting.func(e.target.value);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'slider':
|
||||||
|
thisSetting.inputElement = document.createElement('input');
|
||||||
|
thisSetting.inputElement.type = 'range';
|
||||||
|
thisSetting.inputElement.min = thisSetting.min ?? 0;
|
||||||
|
thisSetting.inputElement.max = thisSetting.max ?? 100;
|
||||||
|
thisSetting.inputElement.step = thisSetting.step ?? 1;
|
||||||
|
if(thisSetting.func){
|
||||||
|
thisSetting.inputElement.addEventListener('input', e => {
|
||||||
|
thisSetting.func(e.target.value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
thisSetting.divContainer.remove();
|
thisSetting.divContainer.remove();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
parent.appendChild(thisSetting.divContainer);
|
||||||
|
thisSetting.labelElement.innerHTML = thisSetting.label;
|
||||||
|
thisSetting.labelElement.htmlFor = thisSetting.id;
|
||||||
|
thisSetting.inputElement.id = thisSetting.id;
|
||||||
|
|
||||||
|
thisSetting.inputElement.style.margin = `0 0.5em 0 ${indent}em`;
|
||||||
|
thisSetting.divContainer.prepend(thisSetting.inputElement);
|
||||||
|
|
||||||
if(thisSetting.children){
|
if(thisSetting.children){
|
||||||
for(let child of thisSetting.children){
|
for(let child of thisSetting.children){
|
||||||
addSettingEntry(child, parent, indent+1);
|
addSettingEntry(child, parent, indent+1);
|
||||||
|
|||||||
@@ -90,6 +90,10 @@ a:visited{
|
|||||||
columns: 2;
|
columns: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#legendKeyColors{
|
||||||
|
overflow-wrap:break-word;
|
||||||
|
}
|
||||||
|
|
||||||
#legend{
|
#legend{
|
||||||
transition: transform 400ms cubic-bezier(0.445, 0.050, 0.550, 0.950);
|
transition: transform 400ms cubic-bezier(0.445, 0.050, 0.550, 0.950);
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -143,10 +147,19 @@ a:visited{
|
|||||||
background: linear-gradient(#444, #333);
|
background: linear-gradient(#444, #333);
|
||||||
}
|
}
|
||||||
|
|
||||||
.inlineSvg {
|
.colorKey, .gradientKey{
|
||||||
|
border:1px #858585 solid;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 2px 1px;
|
||||||
|
padding: 1px 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inlineSvg, .colorKey>span{
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
vertical-align: -0.1em;
|
vertical-align: -0.1em;
|
||||||
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.poi{
|
.poi{
|
||||||
|
|||||||
Reference in new issue
Block a user