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];
}
case "max-speed":
case "maxspeed":
if (!object.maxspeed) {
if (object.type === 'RouteTrack') return modeDef.options['unknown'][0];
return modeDef.options['derail'][0];

View File

@ -5,28 +5,29 @@ import SceneryContext from "./SceneryContext";
const GradientsContext = createContext();
export default GradientsContext;
function getMinMax(trackColorMode, scenery) {
if (trackColorMode === 'elevation') {
if (!scenery) return null;
if (scenery.trackElevationBounds.minY > scenery.trackElevationBounds.maxY) return null;
function getMinMax(trackColorMode, scenery, gradient) {
const sceneryGradientBounds = scenery?.dynamicGradientBounds?.[trackColorMode];
if (!sceneryGradientBounds) return null;
if (sceneryGradientBounds.min > sceneryGradientBounds.max) return null;
const min = Math.max(sceneryGradientBounds.min, gradient.limitMin ?? -Infinity);
const initialMax = Math.min(sceneryGradientBounds.max, gradient.limitMax ?? Infinity);
let diff = Math.max(initialMax - min, 1);
const min = scenery.trackElevationBounds.minY;
// 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];
}
return null;
}
function createGradientSettings(trackColorMode, scenery, def) {
if ('gradient' in def) return def.gradient;
if ('dynamicGradient' in def) {
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 max = gradient.max[i];

View File

@ -176,29 +176,30 @@ const Constants = {
'slope': {
name: 'Slope',
type: 'gradient',
gradient: {
base: [128, 0, 128],
diff: [0, 25.6, 0],
legendMin: 0,
legendMax: 10,
dynamicGradient: {
min: [128, 0, 128],
max: [128, 255, 128],
defaultMin: 0,
defaultMax: 10,
limitMax: 40,
unit: '‰',
startLegendAt0: false,
}
},
'max-speed': {
'maxspeed': {
name: 'Max speed',
type: 'gradient',
gradient: {
base: [128, 0, 128],
diff: [0, 1.5, 0],
legendMin: 0,
legendMax: 170,
dynamicGradient: {
min: [128, 0, 0],
max: [128, 255, 128],
defaultMin: 0,
defaultMax: 170,
unit: 'km/h',
startLegendAt0: false,
},
options: {
'derail': ['#f22', 'Derail track'],
'unknown': ['#aaa', 'Unknown speed'],
'unknown': ['#f88', 'Unknown speed'],
}
},
'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 DynamicGradientBoundsItem from './dynamic-gradient-bounds-item.js';
export default class Scenery
{
@ -6,7 +7,11 @@ export default class Scenery
signalBoxes = [];
spawnPoints = [];
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;
getName() {
@ -38,7 +43,7 @@ export default class Scenery
this._updateBounds(object.getRenderBounds());
}
if (object.category === 'tracks') {
this._updateTrackElevationBounds(object);
this._updateDynamicGradientBounds(object);
}
}
@ -92,10 +97,13 @@ export default class Scenery
});
}
_updateTrackElevationBounds(object) {
[object.points.start, object.points.end].forEach(point => {
if (point.y < this.trackElevationBounds.minY) this.trackElevationBounds.minY = point.y;
if (point.y > this.trackElevationBounds.maxY) this.trackElevationBounds.maxY = point.y;
_updateDynamicGradientBounds(trackObject) {
[trackObject.points.start, trackObject.points.end].forEach(point => {
this.dynamicGradientBounds.elevation.update(point.y);
})
this.dynamicGradientBounds.maxspeed.update(trackObject.maxspeed);
this.dynamicGradientBounds.slope.update(trackObject.start_slope);
this.dynamicGradientBounds.slope.update(trackObject.end_slope);
}
}