Merge pull request #21 from PyroNicampt/poi_system

PoI System
This commit is contained in:
Pyro Nicampt authored and GitHub committed 2024-12-28 22:13:25 -06:00
commit c6fb27f690
9 files changed
+6538 -6241

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
'use strict';
export const gradeColors = {
grade_flat: '#fff',
grade_0: '#f4f7cd',
grade_1: '#f8ff99',
grade_2: '#edfc1b',
grade_3: '#fcb520',
grade_4: '#f07837',
grade_5: '#e66441',
grade_6: '#ce3f57',
grade_7: '#c33577',
grade_8: '#a3008d',
grade_9: '#690097',
}
+59 -40
View File
@@ -18,10 +18,10 @@
<div id="mapNavigator"> <div id="mapNavigator">
<svg id="map" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 10000 10000"> <svg id="map" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 10000 10000">
<style> <style>
.sign{ .fixedScale{
transform: scale(var(--signScale)); transform: scale(var(--markerScale));
} }
.sign, .rail, .signText{ .sign, .rail, .signText, .poi{
cursor: default; cursor: default;
} }
.signText{ .signText{
@@ -105,84 +105,103 @@
.gradeSign.grade_9{ .gradeSign.grade_9{
fill: var(--grade_9); fill: var(--grade_9);
} }
.cullThreshold_0{
display: var(--cullThreshold_state_0);
}
.cullThreshold_1{
display: var(--cullThreshold_state_1);
}
.cullThreshold_2{
display: var(--cullThreshold_state_2);
}
.cullThreshold_3{
display: var(--cullThreshold_state_3);
}
.cullThreshold_4{
display: var(--cullThreshold_state_4);
}
.cullThreshold_5{
display: var(--cullThreshold_state_5);
}
.cullThreshold_6{
display: var(--cullThreshold_state_6);
}
</style> </style>
<style id="zoomStyle"></style> <style id="zoomStyle"></style>
<defs> <defs>
<g id="gradeArrow" transform="scale(10)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="gradeArrow" transform="scale(30)" viewBox="0 0 10 10" >
<path d="M 0 -7 L 3.8 4 L -3.8 4 z" fill="black"/> <path d="M 0 -7 L 3.8 4 L -3.8 4 z" fill="black"/>
<path d="M 0 -5 L 3 4 L 0 5 L -3 4 z"/> <path d="M 0 -5 L 3 4 L 0 5 L -3 4 z"/>
</g> </g>
<g id="speedSign" transform="scale(15)" viewBox="0 0 10 10" refX="5" refY="5" style="fill:#fff; stroke:#e73819"> <g id="speedSign" transform="scale(15)" viewBox="0 0 10 10" style="fill:#fff; stroke:#e73819">
<circle cx="0" cy="0" r="3"/> <circle cx="0" cy="0" r="3"/>
</g> </g>
<g id="speedSignWarning" transform="scale(15)" viewBox="0 0 10 10" refX="5" refY="5" style="fill:#702012; stroke:#e73819"> <g id="speedSignWarning" transform="scale(15)" viewBox="0 0 10 10" style="fill:#702012; stroke:#e73819">
<circle cx="0" cy="0" r="3"/> <circle cx="0" cy="0" r="3"/>
</g> </g>
<g id="speedSign_1" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_1" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSignWarning"></use> <use href="#speedSignWarning"></use>
<text class="signText" style="fill:#fff;transform:translate(-15px, 20px)">1</text> <text class="signText" style="fill:#fff;transform:translate(-15px, 20px)">1</text>
</g> </g>
<g id="speedSign_2" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_2" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSignWarning"></use> <use href="#speedSignWarning"></use>
<text class="signText" style="fill:#fff;transform:translate(-15px, 20px)">2</text> <text class="signText" style="fill:#fff;transform:translate(-15px, 20px)">2</text>
</g> </g>
<g id="speedSign_3" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_3" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSignWarning"></use> <use href="#speedSignWarning"></use>
<text class="signText" style="fill:#fff;transform:translate(-15px, 20px)">3</text> <text class="signText" style="fill:#fff;transform:translate(-15px, 20px)">3</text>
</g> </g>
<g id="speedSign_4" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_4" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use> <use href="#speedSign"></use>
<text class="signText" style="transform:translate(-15px, 20px)">4</text> <text class="signText" style="transform:translate(-15px, 20px)">4</text>
</g> </g>
<g id="speedSign_5" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_5" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use> <use href="#speedSign"></use>
<text class="signText" style="transform:translate(-16px, 20px)">5</text> <text class="signText" style="transform:translate(-16px, 20px)">5</text>
</g> </g>
<g id="speedSign_6" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_6" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use> <use href="#speedSign"></use>
<text class="signText" style="transform:translate(-15px, 20px)">6</text> <text class="signText" style="transform:translate(-15px, 20px)">6</text>
</g> </g>
<g id="speedSign_7" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_7" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use> <use href="#speedSign"></use>
<text class="signText" style="transform:translate(-15px, 20px)">7</text> <text class="signText" style="transform:translate(-15px, 20px)">7</text>
</g> </g>
<g id="speedSign_8" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_8" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use> <use href="#speedSign"></use>
<text class="signText" style="transform:translate(-16px, 20px)">8</text> <text class="signText" style="transform:translate(-16px, 20px)">8</text>
</g> </g>
<g id="speedSign_9" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_9" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use> <use href="#speedSign"></use>
<text class="signText" style="transform:translate(-15px, 20px)">9</text> <text class="signText" style="transform:translate(-15px, 20px)">9</text>
</g> </g>
<g id="speedSign_10" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_10" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use> <use href="#speedSign"></use>
<text class="signText" style="transform:translate(-35px, 20px)">10</text> <text class="signText" style="transform:translate(-35px, 20px)">10</text>
</g> </g>
<g id="speedSign_12" transform="scale(1)" viewBox="0 0 10 10" refX="5" refY="5"> <g id="speedSign_12" transform="scale(3)" viewBox="0 0 10 10">
<use href="#speedSign"></use> <use href="#speedSign"></use>
<text class="signText" style="transform:translate(-35px, 20px)">12</text> <text class="signText" style="transform:translate(-35px, 20px)">12</text>
</g> </g>
<g id="mrk_office" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="M10 3.8l5.56 6.03h-1.66v5.2h-2.51v-3.93h-2.78v3.93h-2.51v-5.2h-1.66z" fill="#fff"></path>
</g>
<g id="mrk_water" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="M10 2c-1.02 2.01-7.08 14.38 0 14.38 7.08 0 1.02-12.46 0-14.38z" fill="#fff"></path>
</g>
<g id="mrk_coal" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="M10.75 2.98 7.85 4.02 5.64 7.39 5.74 7.54 7.08 7.06 10.14 9.03 10.47 8.6 13.67 7.4 14.65 8.03 14.94 7.21 13.65 4.97ZM7.07 7.6 4.18 8.64 1.96 12.01 3.49 15.33h6.78l.99-3.5-1.3-2.25zm6.61.47-2.9 1.04-.27.4.17.13 1.22 2.21-.91 3.48h5.88l.99-3.02-1.3-2.25z" fill="#fff"></path>
</g>
<g id="mrk_landmark" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="m11.39 1.43c-.2-.09-.42 0-.51.2L4.22 16.88c-.09.2 0 .42.2.5.2.09.42 0 .51-.2L11.59 1.93c.09-.2 0-.42-.2-.51zm.53.93-2.39 5.48c.21 0 .42 0 .62.01.2.01.41.02.61.03.2.01.4.03.6.05.2.02.39.04.59.06.19.02.39.05.58.08.19.03.38.06.57.1.09.02.18.04.28.05l2.12-2.56c-.01-.01-.02-.03-.04-.05C15.31 5.46 15.18 5.3 15.05 5.14 14.91 4.99 14.78 4.83 14.64 4.68 14.5 4.53 14.36 4.39 14.22 4.24 14.08 4.1 13.93 3.96 13.79 3.82 13.64 3.68 13.49 3.55 13.34 3.42 13.27 3.35 13.19 3.29 13.12 3.23 13.04 3.16 12.96 3.1 12.88 3.04 12.81 2.98 12.73 2.92 12.65 2.86 12.57 2.8 12.49 2.74 12.41 2.69 12.33 2.63 12.25 2.57 12.16 2.52 12.08 2.47 12 2.41 11.92 2.36Zm3.37 4.06-2.12 2.56c.1.02.19.04.29.06.19.04.37.08.55.13.18.04.37.09.55.14.18.05.36.1.54.15.18.05.36.1.53.16.18.05.35.11.53.17.18.06.35.12.53.17.17.06.35.12.52.18.35.12.69.25 1.04.37C18 10.15 17.76 9.79 17.51 9.44 17.27 9.09 17.02 8.74 16.78 8.39 16.65 8.22 16.53 8.05 16.4 7.88 16.28 7.7 16.15 7.53 16.02 7.36 15.9 7.2 15.77 7.03 15.64 6.86 15.53 6.71 15.41 6.57 15.29 6.42Z" fill="#fff"></path>
</g>
<g id="mrk_shop" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="M9.25 17.9V16.35C8.29 16.26 7.75 15.91 7.12 15.31 6.49 14.71 6.14 13.86 6.06 12.75l2.2-.49c.03.65.21 1.14.54 1.49.33.35.75.52 1.24.52.47 0 .81-.13 1.03-.4.23-.27.34-.55.34-.85 0-.39-.11-.71-.34-.94-.23-.24-.52-.46-.88-.65L8.66 10.62C7.91 10.23 7.33 9.75 6.9 9.2 6.48 8.65 6.27 7.92 6.27 7.02 6.27 6.37 6.41 5.8 6.68 5.31 6.97 4.8 7.35 4.4 7.83 4.1 8.32 3.8 8.63 3.61 9.25 3.54V2.1h1.65v1.48c.91.12 1.41.49 1.94 1.1.53.6.82 1.39.88 2.36L11.65 7.47C11.62 6.87 11.45 6.41 11.15 6.08 10.86 5.75 10.5 5.58 10.07 5.58 9.66 5.58 9.33 5.7 9.08 5.94 8.84 6.17 8.72 6.48 8.72 6.88c0 .36.11.65.32.86.21.21.51.42.88.63l1.63.86c.83.45 1.43.95 1.82 1.51.39.55.58 1.23.58 2.02 0 .6-.13 1.16-.4 1.67-.25.51-.63.95-1.12 1.3-.48.33-.84.54-1.53.63v1.4z" fill="#fff"></path>
</g>
<g id="mrk_garage" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="M10 2.88 2.98 6.42 2.98 15.1 5.53 15.1 5.53 8.91 14.47 8.91 14.47 15.1 17.02 15.1 17.02 6.42 10 2.88z" fill="#fff"></path>
</g>
<g id="mrk_service" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="m8.92 1.99q3.29 0 4.68 1.13 1.39 1.1 1.39 2.74 0 2.06-1.78 3.36-.67.5-1.63.91l-.38.17q-.17.07-.34 1.01-.14.91-.14 1.32-.12.12-.7.29-.55.14-.94.14-.36 0-.55-.05-.26-.43-.26-1.87 0-1.44.12-1.85 3.79-.91 3.79-3.07 0-1.87-3.46-1.87-1.22 0-1.78.24-.14-.24-.14-1.01 0-.77.22-1.3.65-.29 1.9-.29zm-.7 15.5q-.46-.48-.46-1.15 0-.67.46-1.13.48-.48 1.15-.48.67 0 1.13.48.48.46.48 1.13 0 .67-.48 1.15-.46.46-1.13.46-.67 0-1.15-.46z" fill="#fff"></path>
</g>
<g id="mrk_service_repair" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="m12.88 2.82c-.1.01-.2.05-.28.13l-2.19 2.19c-.15.16-.17.39-.03.53l1.12 1.12c.29-.29.37.07.57.58l-1.61 1.6 1.77 1.72 1.7-1.7c.44.03.82-.06.99.11.22.22.19.59-.06.85l-.02.02c1.48 1.26 3.85-1.11 2.59-2.59l-.02.02c-.25.25-.63.28-.85.06-.3-.3.23-1.31-.61-2.15l-.1-.1c-.73-.74-2.07-.7-1.6-1.18l-1.12-1.12c-.06-.06-.16-.09-.25-.09zM5.63 3.68c-.28 0-.55.05-.83.14l2.02 2.02-1.85 1.86-2.02-2.02c-.72 2.23 1.45 4.34 3.67 3.55l7.27 7.27c.46.47 1.22.47 1.71-.02.49-.49.5-1.25.03-1.72L8.36 7.49c.69-1.94-.84-3.84-2.73-3.81zm2.41 7.72-3.36 3.35c-.47.47-.47 1.23.02 1.72.49.49 1.25.49 1.72.02l3.38-3.38z" fill="#fff"></path>
</g>
<g id="mrk_service_diesel" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="m12.67 2.93c-.5-.01-1 .09-1.46.31L9.86 3.88 9.76 3.66 7.64 4.67 7.18 4.02C7.09 3.89 6.92 3.86 6.79 3.95L5.55 4.8C5.43 4.89 5.4 5.07 5.48 5.2L5.87 5.75 5.51 6.22 2.7 10.29c-.2.3-.18.7.06.97-.4.74-.49 1.54-.3 2.26.22.86.77 1.6 1.5 2.16 1.46 1.12 3.68 1.56 5.63.5 1.07-.59 2.19-.79 2.99-.64.8.14 1.26.52 1.39 1.32.05.33.36.55.69.5.33-.05.55-.36.5-.69-.2-1.27-1.2-2.1-2.36-2.31-1.16-.21-2.51.08-3.78.77C7.53 15.95 5.8 15.6 4.69 14.74 4.14 14.32 3.76 13.77 3.62 13.23 3.52 12.83 3.53 12.42 3.73 11.99l1.39.96c.81.56 1.93.36 2.49-.46L10.41 8.46 9.88 5.6 10.53 5.29 10.42 5.05 11.76 4.42C12.2 4.21 12.7 4.17 13.17 4.32L16.71 5.44 17.1 4.2 13.56 3.08C13.27 2.99 12.97 2.94 12.67 2.93Zm4.24 3.31c-.39.63-2.7 4.48 0 4.48 2.7 0 .39-3.88 0-4.48zM6.91 7.47 8.22 7.92 7.2 8.58c-.04.03-.08.06-.11.11l-1.55 2.46c-.1.16-.05.38.11.48.16.1.38.05.48-.11L7.64 9.12 8.94 8.29c.24.29.25.71.02 1.03L7.44 11.52C6.92 12.27 5.93 12.48 5.22 11.99L4.36 11.4Z" fill="#fff"></path>
</g>
<g id="mrk_service_charger" transform="scale(20)" viewBox="0 0 20 20">
<circle r="10"></circle>
<path transform="translate(-10 -10)" d="m7.96 2.6h4.56l-1.97 5.8h3.02L8.61 18.36 9.6 10.25H7.26Z" fill="#fff"></path>
</g>
</defs> </defs>
</svg> </svg>
<svg id="mapMarkers" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 10000 10000"> <svg id="mapMarkers" xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 10000 10000">
@@ -196,13 +215,13 @@
</div> </div>
</div> </div>
<div id="legend" style="display: flex;"> <div id="legend" style="display: flex;">
<div style="display:flex; flex-direction:column; width: 400px;"> <div id="legendContents" style="display:flex; flex-direction:column; width: 400px;">
<div class="panel"> <div class="panel">
Welcome to the WIP community map of <a href="https://www.derailvalley.com/" style="white-space:nowrap;">Derail Valley</a>, a resource for documenting and displaying all tracks and points of interest. Welcome to the WIP community map of <a href="https://www.derailvalley.com/" style="white-space:nowrap;">Derail Valley</a>, a resource for documenting and displaying all tracks and points of interest.
<hr> <hr>
Hover over tracks and objects for detailed information. Hover over tracks and objects for detailed information.
</div> </div>
<div class="panel"> <div class="panel" style="width:330px">
<h3>Key</h3> <h3>Key</h3>
<hr> <hr>
<div id="legendKey"></div> <div id="legendKey"></div>
+289 -75
View File
@@ -3,6 +3,7 @@
import * as Vector from './js/vectorlib.js'; import * as Vector from './js/vectorlib.js';
import * as Bezier from './js/bezierlib.js'; import * as Bezier from './js/bezierlib.js';
import * as Utils from './js/utillib.js'; import * as Utils from './js/utillib.js';
import * as Config from './config.js';
// CONFIG // CONFIG
const bezierGradeResolution = 80; const bezierGradeResolution = 80;
@@ -57,6 +58,14 @@ map.appendChild(map_rails);
//map_markers.setAttribute('id', 'map_markers'); //map_markers.setAttribute('id', 'map_markers');
//map.appendChild(map_markers); //map.appendChild(map_markers);
const map_markers = document.getElementById('mapMarkers'); const map_markers = document.getElementById('mapMarkers');
const poiLayers = {
container: document.createElementNS(svgns, 'g'),
stations: document.createElementNS(svgns, 'g'),
service: document.createElementNS(svgns, 'g'),
features: document.createElementNS(svgns, 'g'),
landmarks: document.createElementNS(svgns, 'g')
};
let mapData = []; let mapData = [];
let trackNameCounting = {}; let trackNameCounting = {};
@@ -67,8 +76,17 @@ let markers = [];
let currentMapNav = {}; let currentMapNav = {};
let previousMapScale = -1; let previousMapScale = -1;
let MapMatrix = {};
document.addEventListener('DOMContentLoaded', async () => { document.addEventListener('DOMContentLoaded', async () => {
let dynamicStyles = [];
for(let i=0; i<scaleCullThresholds.length; i++){
dynamicStyles.push(`.maxZoomCull_${i}{display:var(--maxZoomCull_state_${i});}`);
dynamicStyles.push(`.minZoomCull_${i}{display:var(--minZoomCull_state_${i});}`);
}
document.getElementById('zoomStyle').innerHTML = dynamicStyles.join('\n');
await loadTrackData('trackdata_dv.json'); await loadTrackData('trackdata_dv.json');
await loadPoiData('poi_dv.json');
mapScrollSetup(); mapScrollSetup();
}); });
@@ -78,6 +96,21 @@ async function loadTrackData(file){
fillSvg(); fillSvg();
} }
async function loadPoiData(file){
poiData = await (await fetch(new Request(file))).json();
for(let layerIdx in poiLayers){
poiLayers[layerIdx].setAttribute('id', 'poi_'+layerIdx);
if(layerIdx == 'container') continue;
poiLayers.container.appendChild(poiLayers[layerIdx]);
}
map_markers.appendChild(poiLayers.container);
for(let poi of poiData){
addPoi(poi);
}
}
/** Handling for the map scrolling and zooming */ /** Handling for the map scrolling and zooming */
function mapScrollSetup(){ function mapScrollSetup(){
let mapNav = {x:currentMapNav.x, y:currentMapNav.y, scale:currentMapNav.scale}; let mapNav = {x:currentMapNav.x, y:currentMapNav.y, scale:currentMapNav.scale};
@@ -149,10 +182,11 @@ function updateMapview(navData = {x:0, y:0, scale:1}){
} }
} }
map_rails.setAttribute('stroke-width', Utils.clamp(trackWidth.base/navData.scale, trackWidth.min, trackWidth.max)); map_rails.setAttribute('stroke-width', Utils.clamp(trackWidth.base/navData.scale, trackWidth.min, trackWidth.max));
styleRoot.style.setProperty('--signScale', Utils.clamp(signSize.base/navData.scale, signSize.min, signSize.max)); styleRoot.style.setProperty('--markerScale', 1/navData.scale);
if(dirtyScale){ if(dirtyScale){
for(let i=0; i<scaleCullThresholds.length; i++){ for(let i=0; i<scaleCullThresholds.length; i++){
styleRoot.style.setProperty(`--cullThreshold_state_${i}`, navData.scale >= scaleCullThresholds[i].scale ? 'initial' : 'none') styleRoot.style.setProperty(`--minZoomCull_state_${i}`, navData.scale >= scaleCullThresholds[i].scale ? 'initial' : 'none');
styleRoot.style.setProperty(`--maxZoomCull_state_${i}`, navData.scale <= scaleCullThresholds[i].scale ? 'initial' : 'none');
} }
} }
previousMapScale = navData.scale; previousMapScale = navData.scale;
@@ -171,16 +205,27 @@ function establishDimensions(){
let maxAlt = -Infinity; let maxAlt = -Infinity;
for(const obj of mapData){ for(const obj of mapData){
for(const point of obj.points){ for(const point of obj.points){
minX = Math.min(minX, point[0].x, point[1].x, point[2].x); minX = Math.min(minX, point.position.x, point.h1.x, point.h2.x);
minY = Math.min(minY, point[0].z, point[1].z, point[2].z); minY = Math.min(minY, point.position.z, point.h1.z, point.h2.z);
minAlt = Math.min(minAlt, point[0].y, point[1].y, point[2].y); minAlt = Math.min(minAlt, point.position.y, point.h1.y, point.h2.y);
maxX = Math.max(maxX, point[0].x, point[1].x, point[2].x); maxX = Math.max(maxX, point.position.x, point.h1.x, point.h2.x);
maxY = Math.max(maxY, point[0].z, point[1].z, point[2].z); maxY = Math.max(maxY, point.position.z, point.h1.z, point.h2.z);
maxAlt = Math.max(maxAlt, point[0].y, point[1].y, point[2].y); maxAlt = Math.max(maxAlt, point.position.y, point.h1.y, point.h2.y);
} }
} }
let width = maxX-minX; let width = maxX-minX;
let height = maxY-minY; let height = maxY-minY;
MapMatrix = {
x1: minX,
x2: maxX,
y1: minY,
y2: maxY,
width: width,
height: height,
fixY: yIn => {
return minY+maxY-yIn;
}
}
map.setAttribute('viewBox', `${minX-padding} ${minY-padding} ${width+2*padding} ${height+2*padding}`); map.setAttribute('viewBox', `${minX-padding} ${minY-padding} ${width+2*padding} ${height+2*padding}`);
map_markers.setAttribute('viewBox', map.getAttribute('viewBox')); map_markers.setAttribute('viewBox', map.getAttribute('viewBox'));
updateMapview({ updateMapview({
@@ -193,9 +238,9 @@ function establishDimensions(){
// Flip the z coordinate so the map doesn't display upside-down // Flip the z coordinate so the map doesn't display upside-down
for(const obj of mapData){ for(const obj of mapData){
for(const point of obj.points){ for(const point of obj.points){
point[0].z = minY+maxY-point[0].z; point.position.z = minY+maxY-point.position.z;
point[1].z = minY+maxY-point[1].z; point.h1.z = minY+maxY-point.h1.z;
point[2].z = minY+maxY-point[2].z; point.h2.z = minY+maxY-point.h2.z;
} }
} }
} }
@@ -210,6 +255,7 @@ function fillSvg(){
} }
} }
sortTracks(); sortTracks();
setTrackColorMode('Grade');
drawMarkers(); drawMarkers();
} }
@@ -221,12 +267,29 @@ function drawTracks(bezierData){
}else{ }else{
trackNameCounting[bezierData.name]++; trackNameCounting[bezierData.name]++;
} }
let yardData = /\[(#)\]|\[(Y)\]_\[(.*?)\]_\[((.*?)-(.*?)-(.*?))\]/.exec(bezierData.name);
if(yardData){
if(yardData[1]){ // track is [#], thus in yard limits, but not a specific designated yard.
bezierData.isYard = true;
}else if(yardData[2]){ // track is [Y], and is a specific yard.
bezierData.isYard = true;
bezierData.yardStation = yardData[3]; // Which station the track belongs to
bezierData.yardDesignation = yardData[4]; // Full yard designation, used on signs
bezierData.yardName = yardData[5]; // A yard, B yard, C yard, etc.
bezierData.yardSidingNumber = yardData[6]; // Which siding within the yard
bezierData.yardUsage = yardData[7]; // Outbound? Parking? Inbound? Loading? That's this.
}
}else if(/Turntable Track/.test(bezierData.name)){
bezierData.isTurntable = true;
}else if(/\[track (diverging|through)\]/.test(bezierData.name)){
bezierData.isPoint = true;
}
for(let i=0; i+1<bezierData.points.length; i++){ for(let i=0; i+1<bezierData.points.length; i++){
const bezStart = bezierData.points[i][0]; const bezStart = bezierData.points[i].position;
const bezHandle1 = bezierData.points[i][2]; const bezHandle1 = bezierData.points[i].h2;
const bezHandle2 = bezierData.points[i+1][1]; const bezHandle2 = bezierData.points[i+1].h1;
const bezEnd = bezierData.points[i+1][0]; const bezEnd = bezierData.points[i+1].position;
let newBezier = document.createElementNS(svgns, 'path'); bezierData.points[i].element = document.createElementNS(svgns, 'path');
let genPath = 'M '; let genPath = 'M ';
genPath += `${bezStart.x} ${bezStart.z} `; genPath += `${bezStart.x} ${bezStart.z} `;
genPath += `C ${bezHandle1.x} ${bezHandle1.z} ${bezHandle2.x} ${bezHandle2.z} ${bezEnd.x} ${bezEnd.z} `; genPath += `C ${bezHandle1.x} ${bezHandle1.z} ${bezHandle2.x} ${bezHandle2.z} ${bezEnd.x} ${bezEnd.z} `;
@@ -235,18 +298,20 @@ function drawTracks(bezierData){
bezierData.points[i].bezLength = Bezier.estimateLength(bezStart, bezHandle1, bezHandle2, bezEnd, bezierLengthResolution); bezierData.points[i].bezLength = Bezier.estimateLength(bezStart, bezHandle1, bezHandle2, bezEnd, bezierLengthResolution);
bezierData.points[i].curvature = Bezier.estimateCurvature(bezStart, bezHandle1, bezHandle2, bezEnd, bezierCurvatureResolution); bezierData.points[i].curvature = Bezier.estimateCurvature(bezStart, bezHandle1, bezHandle2, bezEnd, bezierCurvatureResolution);
bezierData.points[i].postedSpeed = Utils.radiusToSpeed(1/bezierData.points[i].curvature); bezierData.points[i].postedSpeed = Utils.radiusToSpeed(1/bezierData.points[i].curvature);
newBezier.setAttribute('d', genPath); if(bezierData.isYard || bezierData.isTurntable) bezierData.points[i].postedSpeed = Math.min(bezierData.points[i].postedSpeed, 50);
bezierData.points[i].element.setAttribute('d', genPath);
newBezier.classList.add('rail'); bezierData.points[i].element.classList.add('rail');
newBezier.classList.add(bezierData.points[i].gradeClass != null ? 'grade_'+bezierData.points[i].gradeClass : 'grade_flat'); //bezierData.points[i].element.setAttribute('stroke', Config.gradeColors[bezierData.points[i].gradeClass ? 'grade_'+bezierData.points[i].gradeClass : 'grade_flat']);
//bezierData.points[i].element.classList.add(bezierData.points[i].gradeClass != null ? 'grade_'+bezierData.points[i].gradeClass : 'grade_flat');
//newBezier.setAttribute('stroke', Utils.rgba2hex(bezierData.points[i].grade*50*0,(bezStart.y-113)/(252-113),bezierData.points[i].bezLength*0.01*0,1.0)); //bezierData.points[i].element.setAttribute('stroke', Utils.rgba2hex(bezierData.points[i].grade*50*0,(bezStart.y-113)/(252-113),bezierData.points[i].bezLength*0.01*0,1.0));
//newBezier.setAttribute('stroke', Utils.rgba2hex((1/bezierData.points[i].curvature) * 100,0,0,1.0)); //bezierData.points[i].element.setAttribute('stroke', Utils.rgba2hex((1/bezierData.points[i].curvature) * 100,0,0,1.0));
//newBezier.setAttribute('stroke', trackCol); //bezierData.points[i].element.setAttribute('stroke', trackCol);
//newBezier.setAttribute('data-grade', bezierData.points[i].grade); //bezierData.points[i].element.setAttribute('data-grade', bezierData.points[i].grade);
//newBezier.setAttribute('data-length', bezierData.points[i].bezLength); //bezierData.points[i].element.setAttribute('data-length', bezierData.points[i].bezLength);
//newBezier.setAttribute('data-maxradius', 1/bezierData.points[i].curvature); //bezierData.points[i].element.setAttribute('data-maxradius', 1/bezierData.points[i].curvature);
tracks.push([(bezStart.y+bezEnd.y)*0.5, newBezier]); tracks.push([(bezStart.y+bezEnd.y)*0.5, bezierData.points[i].element]);
// Curve tooltip // Curve tooltip
let title = document.createElementNS(svgns, 'title'); let title = document.createElementNS(svgns, 'title');
@@ -258,7 +323,7 @@ function drawTracks(bezierData){
`Length: ${Math.round(bezierData.points[i].bezLength*10)/10} meters`, `Length: ${Math.round(bezierData.points[i].bezLength*10)/10} meters`,
`Altitude: ${Math.round((bezStart.y+bezEnd.y)*0.5)} meters`, `Altitude: ${Math.round((bezStart.y+bezEnd.y)*0.5)} meters`,
].join('\n'); ].join('\n');
newBezier.appendChild(title); bezierData.points[i].element.appendChild(title);
} }
// Add Signage // Add Signage
@@ -283,17 +348,17 @@ function drawTracks(bezierData){
}else{ }else{
//Grade Signs //Grade Signs
if(trackZoneData.gradeClass != bezierData.points[i].gradeClass || trackZoneData.gradeDirection != Math.sign(bezierData.points[i].grade) || trackZoneData.gradeSectionLength > maxSectionLength || zI > 0){ if(trackZoneData.gradeClass != bezierData.points[i].gradeClass || trackZoneData.gradeDirection != Math.sign(bezierData.points[i].grade) || trackZoneData.gradeSectionLength > maxSectionLength || zI > 0){
if(trackZoneData.gradeClass != null){ if(trackZoneData.gradeClass != 'flat'){
let offset = Math.floor((trackZoneData.gradeCount+1)/2)+1; let offset = Math.floor((trackZoneData.gradeCount+1)/2)+1;
let center = trackZoneData.gradeCount % 2 ? 1 : 0.5; let center = trackZoneData.gradeCount % 2 ? 1 : 0.5;
let b1 = bezierData.points[(i+zI)-offset][0]; let b1 = bezierData.points[(i+zI)-offset].position;
let b2 = bezierData.points[(i+zI)-offset][2]; let b2 = bezierData.points[(i+zI)-offset].h2;
let b3 = bezierData.points[(i+zI)+1-offset][1]; let b3 = bezierData.points[(i+zI)+1-offset].h1;
let b4 = bezierData.points[(i+zI)+1-offset][0]; let b4 = bezierData.points[(i+zI)+1-offset].position;
markers.push({ markers.push({
type: 'grade', type: 'grade',
value: bezierData.points[(i+zI)-offset].grade, value: bezierData.points[(i+zI)-offset].grade,
class: 'grade_'+trackZoneData.gradeClass, gradeClass: trackZoneData.gradeClass,
position: Bezier.evaluatePoint(b1, b2, b3, b4, center), position: Bezier.evaluatePoint(b1, b2, b3, b4, center),
sectionLength: trackZoneData.gradeSectionLength, sectionLength: trackZoneData.gradeSectionLength,
cullLevel: Math.round(scaleCullThresholds.length-1-trackZoneData.gradeSectionLength/scaleCullBaseSectionLength), cullLevel: Math.round(scaleCullThresholds.length-1-trackZoneData.gradeSectionLength/scaleCullBaseSectionLength),
@@ -311,10 +376,10 @@ function drawTracks(bezierData){
if(trackZoneData.speed != bezierData.points[i].postedSpeed || trackZoneData.speedSectionLength > maxSectionLength || zI > 0){ if(trackZoneData.speed != bezierData.points[i].postedSpeed || trackZoneData.speedSectionLength > maxSectionLength || zI > 0){
let offset = Math.floor((trackZoneData.speedCount+1)/2)+1; let offset = Math.floor((trackZoneData.speedCount+1)/2)+1;
let center = trackZoneData.speedCount % 2 ? 0.75 : 0.25; let center = trackZoneData.speedCount % 2 ? 0.75 : 0.25;
let b1 = bezierData.points[(i+zI)-offset][0]; let b1 = bezierData.points[(i+zI)-offset].position;
let b2 = bezierData.points[(i+zI)-offset][2]; let b2 = bezierData.points[(i+zI)-offset].h2;
let b3 = bezierData.points[(i+zI)+1-offset][1]; let b3 = bezierData.points[(i+zI)+1-offset].h1;
let b4 = bezierData.points[(i+zI)+1-offset][0]; let b4 = bezierData.points[(i+zI)+1-offset].position;
let cullLevel = Math.round(scaleCullThresholds.length-1-trackZoneData.gradeSectionLength/scaleCullBaseSectionLengthSpeedboard); let cullLevel = Math.round(scaleCullThresholds.length-1-trackZoneData.gradeSectionLength/scaleCullBaseSectionLengthSpeedboard);
let speedDelta = Math.max(0, let speedDelta = Math.max(0,
(bezierData.points[i].postedSpeed != null && bezierData.points[i].postedSpeed < 100) ? bezierData.points[i].postedSpeed-trackZoneData.speed : 0, (bezierData.points[i].postedSpeed != null && bezierData.points[i].postedSpeed < 100) ? bezierData.points[i].postedSpeed-trackZoneData.speed : 0,
@@ -330,7 +395,7 @@ function drawTracks(bezierData){
value: trackZoneData.speed, value: trackZoneData.speed,
position: Bezier.evaluatePoint(b1, b2, b3, b4, center), position: Bezier.evaluatePoint(b1, b2, b3, b4, center),
sectionLength: trackZoneData.speedSectionLength, sectionLength: trackZoneData.speedSectionLength,
cullLevel: cullLevel, cullLevel: cullLevel == 1 ? 2 : cullLevel,
tangent: Bezier.evaluateVelocity(b1, b2, b3, b4, center) tangent: Bezier.evaluateVelocity(b1, b2, b3, b4, center)
}); });
resetTzdSpeed(); resetTzdSpeed();
@@ -353,45 +418,194 @@ function sortTracks(){
} }
} }
/** Create the signage */ function setTrackColorMode(mode){
function drawMarkers(){ let modeFunction = null;
const gradeSigns = document.createElementNS(svgns, 'g'); switch(mode){
gradeSigns.setAttribute('id', 'grade_signs'); case 'Track Type':
map_markers.appendChild(gradeSigns); modeFunction = (section, curve) => {
const speedSigns = document.createElementNS(svgns, 'g'); let finalColor = '#aaa';
speedSigns.setAttribute('id', 'speed_signs'); if(curve.isYard && curve.yardDesignation) finalColor = '#4bf';
map_markers.appendChild(speedSigns); else if(curve.isYard) finalColor = '#4fb';
for(const markerData of markers){ else if(curve.isPoint) finalColor = '#fb4';
//if(markerData.skip) continue; else if(curve.isTurntable) finalColor = '#b4f';
let marker = document.createElementNS(svgns, 'g');
let markerImg = document.createElementNS(svgns, 'use');
let title = document.createElementNS(svgns, 'title');
marker.appendChild(markerImg);
marker.appendChild(title);
marker.setAttribute('transform', `translate(${markerData.position.x} ${markerData.position.z})`);
// Grade signs
if(markerData.type == 'grade'){
markerImg.setAttribute('href', '#gradeArrow');
markerImg.classList.add('sign', 'gradeSign');
if(markerData.class) markerImg.classList.add(markerData.class);
const tanLen = Math.sqrt(markerData.tangent.x*markerData.tangent.x + markerData.tangent.z*markerData.tangent.z) * Math.sign(markerData.tangent.y);
const rot = Math.atan2(markerData.tangent.x/tanLen, -markerData.tangent.z/tanLen) * (180/Math.PI);
marker.setAttribute('transform', `translate(${markerData.position.x} ${markerData.position.z}) rotate(${rot})`);
title.innerHTML = `${Math.round(markerData.value*1000)/10}% Grade\nSection Length: ${markerData.sectionLength.toFixed(1)} meters`;
gradeSigns.appendChild(marker);
// Speed Signs
}else if(markerData.type == 'speed'){
markerImg.setAttribute('href', `#speedSign_${Math.round(markerData.value/10)}`);
markerImg.classList.add('sign', 'speedSign');
title.innerHTML = `${Math.round(markerData.value)} km/h\nSection Length: ${markerData.sectionLength.toFixed(1)} meters`;
/*let offset = Vector.normalize({x:markerData.tangent.z, y:0, z:-markerData.tangent.x});
marker.setAttribute('transform', `translate(${markerData.position.x + offset.x*100} ${markerData.position.z + offset.z*50})`);*/
marker.setAttribute('transform', `translate(${markerData.position.x} ${markerData.position.z})`);
speedSigns.appendChild(marker);
}
section.element.setAttribute('stroke', finalColor);
}
break;
case 'Grade':
modeFunction = section => {
section.element.setAttribute('stroke', Config.gradeColors['grade_'+section.gradeClass]);
};
break;
default:
modeFunction = section => {
section.element.setAttribute('stroke', '#fff');
}
break;
}
if(modeFunction !== null){
for(let curve of mapData){
for(let section of curve.points){
if(!section.element) continue;
modeFunction(section, curve);
}
}
}
}
/** Create the signage */
function drawMarkers(){
const signage_container = document.createElementNS(svgns, 'g');
const gradeSigns = document.createElementNS(svgns, 'g');
const speedSigns = document.createElementNS(svgns, 'g');
signage_container.setAttribute('id', 'signage_container');
gradeSigns.setAttribute('id', 'signage_grade');
speedSigns.setAttribute('id', 'signage_speed');
map_markers.appendChild(signage_container);
signage_container.appendChild(gradeSigns);
signage_container.appendChild(speedSigns);
for(const markerData of markers){
//if(markerData.skip) continue;
let markerElement = document.createElementNS(svgns, 'g');
let markerImg = document.createElementNS(svgns, 'use');
let markerTooltip = document.createElementNS(svgns, 'title');
markerElement.appendChild(markerImg);
markerElement.appendChild(markerTooltip);
markerElement.setAttribute('transform', `translate(${markerData.position.x} ${markerData.position.z})`);
markerImg.classList.add('fixedScale');
// Grade signs
if(markerData.type == 'grade'){
markerImg.setAttribute('href', '#gradeArrow');
markerImg.setAttribute('fill', Config.gradeColors['grade_'+markerData.gradeClass]);
const tanLen = Math.sqrt(markerData.tangent.x*markerData.tangent.x + markerData.tangent.z*markerData.tangent.z) * Math.sign(markerData.tangent.y);
const rot = Math.atan2(markerData.tangent.x/tanLen, -markerData.tangent.z/tanLen) * (180/Math.PI);
markerElement.setAttribute('transform', `translate(${markerData.position.x} ${markerData.position.z}) rotate(${rot})`);
markerTooltip.innerHTML = `${Math.round(markerData.value*1000)/10}% Grade\nSection Length: ${markerData.sectionLength.toFixed(1)} meters`;
gradeSigns.appendChild(markerElement);
// Speed Signs
}else if(markerData.type == 'speed'){
markerImg.setAttribute('href', `#speedSign_${Math.round(markerData.value/10)}`);
markerTooltip.innerHTML = `${Math.round(markerData.value)} km/h\nSection Length: ${markerData.sectionLength.toFixed(1)} meters`;
markerElement.setAttribute('transform', `translate(${markerData.position.x} ${markerData.position.z})`);
speedSigns.appendChild(markerElement);
}
if(markerData.cullLevel){ if(markerData.cullLevel){
marker.classList.add(`cullThreshold_${Math.min(scaleCullThresholds.length-1, markerData.cullLevel)}`); markerElement.classList.add(`minZoomCull_${Math.min(scaleCullThresholds.length-1, markerData.cullLevel)}`);
}
}
}
function addPoi(poi, level = 0){
let poiElement = document.createElementNS(svgns, 'g');
let poiDisplay = document.createElementNS(svgns, 'g');
let poiIcon = document.createElementNS(svgns, poi.type == 'station' ? 'text' : 'use');
let poiTooltip = document.createElementNS(svgns, 'title');
poiElement.appendChild(poiDisplay);
poiDisplay.appendChild(poiIcon);
poiElement.appendChild(poiTooltip);
poiElement.setAttribute('transform', `translate(${poi.position.x} ${MapMatrix.fixY(poi.position.z)})`);
poiElement.classList.add('poi');
poiDisplay.classList.add('fixedScale');
if(level > 0) poiElement.classList.add('minZoomCull_2');
switch(poi.type){
case 'station':
poiDisplay.classList.remove('fixedScale');
poiElement.setAttribute('fill', poi.color ?? '#fff');
poiTooltip.innerHTML = poi.name;
poiIcon.innerHTML = poi.shorthand;
poiIcon.setAttribute('style', `font-size:400px; stroke:${poi.stroke ?? '#000'}; stroke-width:40px; paint-order:stroke; text-anchor:middle;`);
poiLayers.stations.prepend(poiElement);
break;
case 'office':
poiIcon.setAttribute('href', '#mrk_office');
if(poi.military){
poiDisplay.setAttribute('fill', '#978e47');
poiTooltip.innerHTML = 'Military Office';
}else if(poi.museum){
poiDisplay.setAttribute('fill', '#b44');
poiTooltip.innerHTML = 'Museum Office';
}else{
poiDisplay.setAttribute('fill', '#c4693e');
poiTooltip.innerHTML = 'Station Office';
}
poiLayers.service.prepend(poiElement);
break;
case 'coal':
poiIcon.setAttribute('href', '#mrk_coal');
poiDisplay.setAttribute('fill', '#202020');
poiTooltip.innerHTML = 'Coal Tower';
poiLayers.service.prepend(poiElement);
break;
case 'water':
poiIcon.setAttribute('href', '#mrk_water');
poiDisplay.setAttribute('fill', '#3fa5ff');
poiTooltip.innerHTML = 'Water Tower';
poiLayers.service.prepend(poiElement);
break;
case 'garage':
poiIcon.setAttribute('href', '#mrk_garage');
poiDisplay.setAttribute('fill', '#8b5dd7');
poiTooltip.innerHTML = poi.name ?? 'Unknown Garage';
poiLayers.service.prepend(poiElement);
poiElement.classList.add('minZoomCull_1');
break;
case 'landmark':
poiIcon.setAttribute('href', '#mrk_landmark');
poiDisplay.setAttribute('fill', '#af5757');
poiTooltip.innerHTML = poi.name ?? 'Unknown Landmark';
poiLayers.landmarks.prepend(poiElement);
poiElement.classList.add('minZoomCull_1');
break;
case 'shop':
poiIcon.setAttribute('href', '#mrk_shop');
poiDisplay.setAttribute('fill', '#4f54e9');
poiTooltip.innerHTML = 'Shop';
poiLayers.service.prepend(poiElement);
poiElement.classList.add('minZoomCull_1');
break;
case 'service':
let serviceTypes = [];
if(poi.repair) serviceTypes.push('Repair');
if(poi.diesel) serviceTypes.push('Diesel');
if(poi.charger) serviceTypes.push('Charger');
if(serviceTypes.length == 0){
poiIcon.setAttribute('href', '#mrk_service');
poiDisplay.setAttribute('fill', '#239a96');
poiTooltip.innerHTML = 'Unknown Service';
}else{
poiIcon.setAttribute('href', '#mrk_service_'+serviceTypes[0].toLowerCase());
poiDisplay.setAttribute('fill', '#239a96');
let bumpOffset = -200*(serviceTypes.length-1);
poiIcon.setAttribute('transform', `translate(${bumpOffset} 0)`);
poiTooltip.innerHTML = serviceTypes[0];
for(let i=1; i<serviceTypes.length; i++){
let subIcon = document.createElementNS(svgns, 'use');
subIcon.setAttribute('href', '#mrk_service_'+serviceTypes[i].toLowerCase());
subIcon.setAttribute('transform', `translate(${i*400+bumpOffset} 0)`);
poiDisplay.appendChild(subIcon);
poiTooltip.innerHTML += (i == serviceTypes.length-1 ? (serviceTypes.length == 2 ? ' & ' : ', & ') : ', ')+serviceTypes[i];
}
poiTooltip.innerHTML += ' Service';
if(serviceTypes.length > 1){
let bgrect = document.createElementNS(svgns, 'rect');
bgrect.setAttribute('width', 400*(serviceTypes.length-1));
bgrect.setAttribute('height', 400);
bgrect.setAttribute('transform', `translate(${-200*(serviceTypes.length-1)} -200)`);
poiDisplay.prepend(bgrect);
}
}
poiLayers.service.prepend(poiElement);
break;
}
if(poi.children){
for(let child of poi.children){
addPoi(child, level+1);
} }
} }
} }
+11 -2
View File
@@ -21,9 +21,12 @@ export function estimateGrade(p1, h1, h2, p2, iterationCount = 50){
for(let i=0; i<=iterationCount; i++){ for(let i=0; i<=iterationCount; i++){
let tan = evaluateVelocity(p1, h1, h2, p2, i/iterationCount); let tan = evaluateVelocity(p1, h1, h2, p2, i/iterationCount);
let newGrade = Math.abs(tan.y)/Math.sqrt(tan.x*tan.x + tan.z*tan.z); let newGrade = Math.abs(tan.y)/Math.sqrt(tan.x*tan.x + tan.z*tan.z);
if(!Number.isNaN(newGrade)){
grade = Math.max(grade, newGrade); grade = Math.max(grade, newGrade);
totalGrade += newGrade; totalGrade += newGrade;
} }
}
if(!grade) return 0;
return grade; return grade;
} }
@@ -59,14 +62,20 @@ export function estimateCurvature(p1, h1, h2, p2, iterationCount = 50){
let curvature = 0; let curvature = 0;
let t = 0; let t = 0;
let vel = {}; let vel = {};
let totalCount = 0;
for(let i=0; i<iterationCount; i++){ for(let i=0; i<iterationCount; i++){
t = i/iterationCount; t = i/iterationCount;
vel = evaluateVelocity(p1, h1, h2, p2, t); vel = evaluateVelocity(p1, h1, h2, p2, t);
curvature += let newCurvature =
Vector.length(Vector.cross(vel, evaluateAcceleration(p1, h1, h2, p2, t))) Vector.length(Vector.cross(vel, evaluateAcceleration(p1, h1, h2, p2, t)))
/Math.pow(Vector.length(vel),3); /Math.pow(Vector.length(vel),3);
if(!Number.isNaN(newCurvature)){
curvature += newCurvature;
totalCount++;
} }
return curvature/iterationCount; }
if(!curvature) return 0.00000001;
return curvature/totalCount;
} }
/** Evaluate the bezier at `t`, getting position. /** Evaluate the bezier at `t`, getting position.
+1
View File
@@ -39,6 +39,7 @@ export function gradeToClass(grade){
return gradeIncrements.length-(j+1); return gradeIncrements.length-(j+1);
} }
} }
return 'flat';
} }
export function radiusToSpeed(radius){ export function radiusToSpeed(radius){
+50 -16
View File
@@ -1,6 +1,7 @@
'use strict'; 'use strict';
import * as Utils from './js/utillib.js'; import * as Utils from './js/utillib.js';
import * as Config from './config.js';
let settingEntries = [ let settingEntries = [
{ {
@@ -8,7 +9,7 @@ let settingEntries = [
id: 'toggle_signage', id: 'toggle_signage',
state: true, state: true,
func: state =>{ func: state =>{
document.getElementById('mapMarkers').style.display = state ? 'unset' : 'none'; document.getElementById('signage_container').style.display = state ? 'unset' : 'none';
}, },
children: [ children: [
{ {
@@ -16,7 +17,7 @@ let settingEntries = [
id: 'toggle_speedSigns', id: 'toggle_speedSigns',
state: true, state: true,
func: state =>{ func: state =>{
document.getElementById('speed_signs').style.display = state ? 'unset' : 'none'; document.getElementById('signage_speed').style.display = state ? 'unset' : 'none';
}, },
}, },
{ {
@@ -24,11 +25,37 @@ let settingEntries = [
id: 'toggle_gradeSigns', id: 'toggle_gradeSigns',
state: true, state: true,
func: state =>{ func: state =>{
document.getElementById('grade_signs').style.display = state ? 'unset' : 'none'; document.getElementById('signage_grade').style.display = state ? 'unset' : 'none';
}, },
} }
], ],
}, },
{
label: 'Points of Interest',
id: 'toggle_poi',
state: true,
func: state => {
document.getElementById('poi_container').style.display = state ? 'unset' : 'none';
},
children: [
{
label: 'Services',
id: 'toggle_services',
state: true,
func: state =>{
document.getElementById('poi_service').style.display = state ? 'unset' : 'none';
},
},
{
label: 'Landmarks',
id: 'toggle_landmarks',
state: true,
func: state =>{
document.getElementById('poi_landmarks').style.display = state ? 'unset' : 'none';
},
},
]
},
{ {
label: 'Track Color', label: 'Track Color',
id: 'radio_trackColor', id: 'radio_trackColor',
@@ -116,27 +143,34 @@ function populateKey(){
}else if(i >= 0){ }else if(i >= 0){
gradeValue += ` - ${getGradeValue(i+1)}%`; gradeValue += ` - ${getGradeValue(i+1)}%`;
} }
let gradeIcon = document.createElement('svg'); addKeyEntry(`<use fill="${Config.gradeColors['grade_'+gradeIndex]}" href="#gradeArrow" transform="rotate(90)"/>`, ` ${gradeValue} Grade`);
gradeIcon.classList.add('inlineSvg');
gradeIcon.setAttribute('viewBox', '-50 -50 100 100');
gradeIcon.innerHTML = `<use class="gradeSign grade_${gradeIndex}" href="#gradeArrow" transform="rotate(90)"/>`;
addKeyEntry(gradeIcon, ` ${gradeValue} Grade`);
} }
let speedSignIcon = document.createElement('svg'); addKeyEntry('<use href="#speedSign_5"/>', ' Speed Limit');
speedSignIcon.classList.add('inlineSvg'); addKeyEntry('<use href="#mrk_office" fill="#c4693e" transform="scale(0.75)"/>', ' Station Office');
speedSignIcon.setAttribute('viewBox', '-50 -50 100 100'); addKeyEntry('<use href="#mrk_shop" fill="#4f54e9" transform="scale(0.75)"/>', ' Shop');
speedSignIcon.innerHTML = `<use class="speedSign" href="#speedSign_5"/>`; addKeyEntry('<use href="#mrk_coal" fill="#202020" transform="scale(0.75)"/>', ' Coal Tower');
addKeyEntry(speedSignIcon, ' Speed Limit'); addKeyEntry('<use href="#mrk_water" fill="#3fa5ff" transform="scale(0.75)"/>', ' Water Tower');
addKeyEntry('<use href="#mrk_landmark" fill="#af5757" transform="scale(0.75)"/>', ' Landmark');
addKeyEntry('<use href="#mrk_garage" fill="#8b5dd7" transform="scale(0.75)"/>', ' Garage');
addKeyEntry('<use href="#mrk_service_repair" fill="#239a96" transform="scale(0.75)"/>', ' Repair Service');
addKeyEntry('<use href="#mrk_service_diesel" fill="#239a96" transform="scale(0.75)"/>', ' Diesel Refuel');
addKeyEntry('<use href="#mrk_service_charger" fill="#239a96" transform="scale(0.75)"/>', ' Electric Charger');
} }
/** /**
* Add an entry to the map key. * Add an entry to the map key.
* @param {Element} keyImage * @param {string} keyImageHTML
* @param {string} keyLabel * @param {string} keyLabel
*/ */
function addKeyEntry(keyImage, keyLabel){ function addKeyEntry(keyImageHTML, keyLabel){
let keyDiv = document.createElement('div'); let keyDiv = document.createElement('div');
if(keyImage) keyDiv.appendChild(keyImage); if(keyImageHTML){
let keyImage = document.createElement('svg');
keyImage.innerHTML = keyImageHTML;
keyImage.classList.add('inlineSvg');
keyImage.setAttribute('viewBox', '-150 -150 300 300');
keyDiv.appendChild(keyImage);
}
keyDiv.innerHTML += keyLabel; keyDiv.innerHTML += keyLabel;
legendKey.appendChild(keyDiv); legendKey.appendChild(keyDiv);
} }
+28 -20
View File
@@ -4,7 +4,7 @@
"type":"station", "type":"station",
"shorthand":"CME", "shorthand":"CME",
"color":"#958177", "color":"#958177",
"position":{"x":15622.98,"y":204.2502,"z":11220.5}, "position":{"x":15622.98,"y":204.2502,"z":10620.5},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -31,7 +31,7 @@
"type":"station", "type":"station",
"shorthand":"CMS", "shorthand":"CMS",
"color":"#6f6a58", "color":"#6f6a58",
"position":{"x":8531.313,"y":156.2595,"z":3448.419}, "position":{"x":9251.313,"y":156.2595,"z":3408.419},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -53,7 +53,7 @@
"type":"station", "type":"station",
"shorthand":"CP", "shorthand":"CP",
"color":"#824e4a", "color":"#824e4a",
"position":{"x":2064.521,"y":145.1,"z":9031.848}, "position":{"x":2064.521,"y":145.1,"z":8231.848},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -74,7 +74,7 @@
"type":"station", "type":"station",
"shorthand":"CS", "shorthand":"CS",
"color":"#dfd7dc", "color":"#dfd7dc",
"position":{"x":9985.819,"y":134.6823,"z":1388.548}, "position":{"x":9785.819,"y":134.6823,"z":1488.548},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -100,6 +100,10 @@
"type":"water", "type":"water",
"position":{"x":10249.08,"y":134.7365,"z":1468.002} "position":{"x":10249.08,"y":134.7365,"z":1468.002}
}, },
{
"type":"water",
"position":{"x":10050.59,"y":134.7859,"z":1370.713}
},
{ {
"name":"Olaf's Garage", "name":"Olaf's Garage",
"type":"garage", "type":"garage",
@@ -112,7 +116,7 @@
"type":"station", "type":"station",
"shorthand":"CW", "shorthand":"CW",
"color":"#f1cdbb", "color":"#f1cdbb",
"position": {"x":1956,"y":122,"z":5692}, "position": {"x":1456,"y":122,"z":5692},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -141,7 +145,7 @@
"type":"station", "type":"station",
"shorthand":"FF", "shorthand":"FF",
"color":"#b2d1ff", "color":"#b2d1ff",
"position":{"x":9435.343,"y":119.1784,"z":13504.95}, "position":{"x":9035.343,"y":119.1784,"z":13504.95},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -169,7 +173,7 @@
"type":"station", "type":"station",
"shorthand":"FM", "shorthand":"FM",
"color":"#ffda62", "color":"#ffda62",
"position":{"x":6027.609,"y":123.8593,"z":6704.711}, "position":{"x":6027.609,"y":123.8593,"z":7074.711},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -186,7 +190,7 @@
"type":"station", "type":"station",
"shorthand":"FRC", "shorthand":"FRC",
"color":"#d3e075", "color":"#d3e075",
"position":{"x":5727.74,"y":144.9063,"z":8757.236}, "position":{"x":6427.74,"y":144.9063,"z":8757.236},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -203,7 +207,7 @@
"type":"station", "type":"station",
"shorthand":"FRS", "shorthand":"FRS",
"color":"#8db16c", "color":"#8db16c",
"position":{"x":5340.794,"y":175.1235,"z":3787.574}, "position":{"x":5890.794,"y":175.1235,"z":3787.574},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -221,7 +225,7 @@
"type":"station", "type":"station",
"shorthand":"GF", "shorthand":"GF",
"color":"#ff9db9", "color":"#ff9db9",
"position":{"x":13002.78,"y":140.2193,"z":10995.55}, "position":{"x":13202.78,"y":140.2193,"z":10495.55},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -254,7 +258,7 @@
"type":"station", "type":"station",
"shorthand":"HB", "shorthand":"HB",
"color":"#ff9db9", "color":"#ff9db9",
"position":{"x":13066.07,"y":113.082,"z":3596.37}, "position":{"x":13066.07,"y":113.082,"z":3876.37},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -292,7 +296,7 @@
"type":"station", "type":"station",
"shorthand":"IME", "shorthand":"IME",
"color":"#ff836f", "color":"#ff836f",
"position":{"x":15041.38,"y":248.062,"z":15341.35}, "position":{"x":15041.38,"y":248.062,"z":15741.35},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -319,7 +323,7 @@
"type":"station", "type":"station",
"shorthand":"IMW", "shorthand":"IMW",
"color":"#dc6f54", "color":"#dc6f54",
"position":{"x":2103.091,"y":133.5531,"z":13359.38}, "position":{"x":2163.091,"y":133.5531,"z":13759.38},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -345,7 +349,7 @@
"type":"station", "type":"station",
"shorthand":"MB", "shorthand":"MB",
"color":"#d6ac71", "color":"#d6ac71",
"position":{"x":12524,"y":215.3452,"z":14749.98}, "position":{"x":12524,"y":215.3452,"z":14349.98},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -364,7 +368,7 @@
"type":"station", "type":"station",
"shorthand":"MF", "shorthand":"MF",
"color":"#ffad6d", "color":"#ffad6d",
"position":{"x":2348.254,"y":159.2693,"z":10823.74}, "position":{"x":2848.254,"y":159.2693,"z":10523.74},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -401,7 +405,7 @@
"type":"station", "type":"station",
"shorthand":"OR", "shorthand":"OR",
"color":"#cf6884", "color":"#cf6884",
"position":{"x":6404.917,"y":143.8076,"z":11133.96}, "position":{"x":6934.917,"y":143.8076,"z":11133.96},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -423,7 +427,7 @@
"type":"station", "type":"station",
"shorthand":"OWC", "shorthand":"OWC",
"color":"#7b6f6e", "color":"#7b6f6e",
"position":{"x":4866.821,"y":122.9323,"z":6291.179}, "position":{"x":4466.821,"y":122.9323,"z":6651.179},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -446,7 +450,7 @@
"type":"station", "type":"station",
"shorthand":"OWN", "shorthand":"OWN",
"color":"#8b715e", "color":"#8b715e",
"position":{"x":11570.04,"y":122.3343,"z":11554.65}, "position":{"x":11570.04,"y":122.3343,"z":11954.65},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -465,7 +469,7 @@
"type":"station", "type":"station",
"shorthand":"SM", "shorthand":"SM",
"color":"#ac9da2", "color":"#ac9da2",
"position": {"x":7821,"y":131,"z":7277}, "position": {"x":8321,"y":131,"z":6707},
"children":[ "children":[
{ {
"type":"office", "type":"office",
@@ -485,6 +489,10 @@
{ {
"type":"water", "type":"water",
"position":{"x":7981.461,"y":131.77,"z":7392.4} "position":{"x":7981.461,"y":131.77,"z":7392.4}
},
{
"type":"water",
"position":{"x":7998.145,"y":131.7769,"z":7477.136}
} }
] ]
}, },
@@ -493,7 +501,7 @@
"type":"station", "type":"station",
"shorthand":"SW", "shorthand":"SW",
"color":"#ffce98", "color":"#ffce98",
"position":{"x":1364.217,"y":147.2043,"z":2194.633}, "position":{"x":964.217,"y":147.2043,"z":1794.633},
"children":[ "children":[
{ {
"type":"office", "type":"office",
+9 -12
View File
@@ -1,18 +1,6 @@
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wdth,wght@0,62.5..100,100..900;1,62.5..100,100..900&family=Protest+Strike&display=swap'); @import url('https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wdth,wght@0,62.5..100,100..900;1,62.5..100,100..900&family=Protest+Strike&display=swap');
:root{ :root{
--grade_flat: #ffff;
--grade_0: #f4f7cd;
--grade_1: #f8ff99;
--grade_2: #edfc1b;
--grade_3: #fcb520;
--grade_4: #f07837;
--grade_5: #e66441;
--grade_6: #ce3f57;
--grade_7: #c33577;
--grade_8: #a3008d;
--grade_9: #690097;
--signScale: 1; --signScale: 1;
--cullThreshold_state_0: none; --cullThreshold_state_0: none;
--cullThreshold_state_1: none; --cullThreshold_state_1: none;
@@ -21,6 +9,8 @@
--cullThreshold_state_4: none; --cullThreshold_state_4: none;
--cullThreshold_state_5: none; --cullThreshold_state_5: none;
--cullThreshold_state_6: none; --cullThreshold_state_6: none;
--absScale: 1;
} }
html, body, #map, #mapContainer, h1, h2, h3, h4, h5{ html, body, #map, #mapContainer, h1, h2, h3, h4, h5{
@@ -89,6 +79,9 @@ a:visited{
#legendKey{ #legendKey{
font-size: 15px; font-size: 15px;
display: flex;
flex-flow: column wrap;
max-height: 310px;
} }
#footer{ #footer{
@@ -128,3 +121,7 @@ a:visited{
height: 1em; height: 1em;
vertical-align: -0.1em; vertical-align: -0.1em;
} }
.poi{
font-variation-settings: "wght" 700;
}
+6076 -6076
View File
File diff suppressed because it is too large. Load diff