Extend dynamic gradients

- Generalized dynamic gradient processing
- Updated max-speed gradient internal name to maxspeed
- Updated "Max speed" and "Slope" gradients to be dynamic
This commit is contained in:
izawartka 2026-08-14 01:20:44 +02:00
parent 3ed1fbe9b7
commit 3a242bb8ad
5 changed files with 59 additions and 33 deletions

View File

@ -186,7 +186,7 @@ function getTrackColor(object, trackColorMode, gradientDef) {
return modeDef.options[modeDef.optionDefault][0]; return modeDef.options[modeDef.optionDefault][0];
} }
case "max-speed": case "maxspeed":
if (!object.maxspeed) { if (!object.maxspeed) {
if (object.type === 'RouteTrack') return modeDef.options['unknown'][0]; if (object.type === 'RouteTrack') return modeDef.options['unknown'][0];
return modeDef.options['derail'][0]; return modeDef.options['derail'][0];

View File

@ -5,28 +5,29 @@ import SceneryContext from "./SceneryContext";
const GradientsContext = createContext(); const GradientsContext = createContext();
export default GradientsContext; export default GradientsContext;
function getMinMax(trackColorMode, scenery) { function getMinMax(trackColorMode, scenery, gradient) {
if (trackColorMode === 'elevation') { const sceneryGradientBounds = scenery?.dynamicGradientBounds?.[trackColorMode];
if (!scenery) return null; if (!sceneryGradientBounds) return null;
if (scenery.trackElevationBounds.minY > scenery.trackElevationBounds.maxY) return null;
const min = scenery.trackElevationBounds.minY; if (sceneryGradientBounds.min > sceneryGradientBounds.max) return null;
// Make sure diff > 0 even on flat sceneries
let diff = Math.max(scenery.trackElevationBounds.maxY - scenery.trackElevationBounds.minY, 1);
// Ensure `diff` is even so that the min, max and middle legend values are integers
diff = Math.ceil(diff / 2)*2;
const max = min + diff;
return [min, max]; const min = Math.max(sceneryGradientBounds.min, gradient.limitMin ?? -Infinity);
} const initialMax = Math.min(sceneryGradientBounds.max, gradient.limitMax ?? Infinity);
return null; let diff = Math.max(initialMax - min, 1);
// Ensure `diff` is even so that the min, max and middle legend values are integers
diff = Math.ceil(diff / 2)*2;
const max = min + diff;
return [min, max];
} }
function createGradientSettings(trackColorMode, scenery, def) { function createGradientSettings(trackColorMode, scenery, def) {
if ('gradient' in def) return def.gradient; if ('gradient' in def) return def.gradient;
if ('dynamicGradient' in def) { if ('dynamicGradient' in def) {
const gradient = def.dynamicGradient; const gradient = def.dynamicGradient;
const [minValue, maxValue] = getMinMax(trackColorMode, scenery) ?? [gradient.defaultMin, gradient.defaultMax]; const [minValue, maxValue] = getMinMax(trackColorMode, scenery, gradient)
?? [gradient.defaultMin, gradient.defaultMax];
const diff = gradient.min.map((min, i) => { const diff = gradient.min.map((min, i) => {
const max = gradient.max[i]; const max = gradient.max[i];

View File

@ -176,29 +176,30 @@ const Constants = {
'slope': { 'slope': {
name: 'Slope', name: 'Slope',
type: 'gradient', type: 'gradient',
gradient: { dynamicGradient: {
base: [128, 0, 128], min: [128, 0, 128],
diff: [0, 25.6, 0], max: [128, 255, 128],
legendMin: 0, defaultMin: 0,
legendMax: 10, defaultMax: 10,
limitMax: 40,
unit: '‰', unit: '‰',
startLegendAt0: false, startLegendAt0: false,
} }
}, },
'max-speed': { 'maxspeed': {
name: 'Max speed', name: 'Max speed',
type: 'gradient', type: 'gradient',
gradient: { dynamicGradient: {
base: [128, 0, 128], min: [128, 0, 0],
diff: [0, 1.5, 0], max: [128, 255, 128],
legendMin: 0, defaultMin: 0,
legendMax: 170, defaultMax: 170,
unit: 'km/h', unit: 'km/h',
startLegendAt0: false, startLegendAt0: false,
}, },
options: { options: {
'derail': ['#f22', 'Derail track'], 'derail': ['#f22', 'Derail track'],
'unknown': ['#aaa', 'Unknown speed'], 'unknown': ['#f88', 'Unknown speed'],
} }
}, },
'elevation': { 'elevation': {

View File

@ -0,0 +1,16 @@
export default class DynamicGradientBoundsItem {
min;
max;
startsAtZero;
constructor(startsAtZero) {
this.min = startsAtZero ? 0 : Infinity;
this.max = -Infinity;
this.startsAtZero = startsAtZero;
}
update(value) {
if (!this.startsAtZero && value < this.min) this.min = value;
if (value > this.max) this.max = value;
}
}

View File

@ -1,4 +1,5 @@
import { ElectrificationResolutionStatus } from './electrification-status.js'; import { ElectrificationResolutionStatus } from './electrification-status.js';
import DynamicGradientBoundsItem from './dynamic-gradient-bounds-item.js';
export default class Scenery export default class Scenery
{ {
@ -6,7 +7,11 @@ export default class Scenery
signalBoxes = []; signalBoxes = [];
spawnPoints = []; spawnPoints = [];
bounds = { minX: Infinity, minZ: Infinity, maxX: -Infinity, maxZ: -Infinity }; bounds = { minX: Infinity, minZ: Infinity, maxX: -Infinity, maxZ: -Infinity };
trackElevationBounds = { minY: Infinity, maxY: -Infinity }; dynamicGradientBounds = {
elevation: new DynamicGradientBoundsItem(),
slope: new DynamicGradientBoundsItem(true),
maxspeed: new DynamicGradientBoundsItem(true)
};
electrificationResolved = ElectrificationResolutionStatus.NOT_RESOLVED; electrificationResolved = ElectrificationResolutionStatus.NOT_RESOLVED;
getName() { getName() {
@ -38,7 +43,7 @@ export default class Scenery
this._updateBounds(object.getRenderBounds()); this._updateBounds(object.getRenderBounds());
} }
if (object.category === 'tracks') { if (object.category === 'tracks') {
this._updateTrackElevationBounds(object); this._updateDynamicGradientBounds(object);
} }
} }
@ -92,10 +97,13 @@ export default class Scenery
}); });
} }
_updateTrackElevationBounds(object) { _updateDynamicGradientBounds(trackObject) {
[object.points.start, object.points.end].forEach(point => { [trackObject.points.start, trackObject.points.end].forEach(point => {
if (point.y < this.trackElevationBounds.minY) this.trackElevationBounds.minY = point.y; this.dynamicGradientBounds.elevation.update(point.y);
if (point.y > this.trackElevationBounds.maxY) this.trackElevationBounds.maxY = point.y;
}) })
this.dynamicGradientBounds.maxspeed.update(trackObject.maxspeed);
this.dynamicGradientBounds.slope.update(trackObject.start_slope);
this.dynamicGradientBounds.slope.update(trackObject.end_slope);
} }
} }