diff --git a/McZapkie/Mover.cpp b/McZapkie/Mover.cpp index 769d0136..72808684 100644 --- a/McZapkie/Mover.cpp +++ b/McZapkie/Mover.cpp @@ -6012,7 +6012,7 @@ double TMoverParameters::TractionForce(double dt) // charakterystyka pradnicy obcowzbudnej (elipsa) - twierdzenie Pitagorasa EngineVoltage = std::sqrt(std::abs(square(tempUmax) - square(tempUmax * Im / tempImax))) * (tempMCP - 1) + (1.0 - Im / tempImax) * tempUmax * (tempMCPN - tempMCP); EngineVoltage /= tempMCPN - 1; - EngineVoltage = std::clamp(EngineVoltage, Im * 0.05, 1000.0 * tmp / std::abs(Im)); + EngineVoltage = safe_clamp(EngineVoltage, Im * 0.05, 1000.0 * tmp / std::abs(Im)); } } @@ -7442,11 +7442,11 @@ void TMoverParameters::CheckEIMIC(double dt) if (MainCtrlActualPos != MainCtrlPos || LastRelayTime > InitialCtrlDelay) { - eimic -= std::clamp(-UniCtrlList[MainCtrlPos].SetCtrlVal + eimic, 0.0, + eimic -= safe_clamp(-UniCtrlList[MainCtrlPos].SetCtrlVal + eimic, 0.0, MainCtrlActualPos == MainCtrlPos ? dt * UniCtrlList[MainCtrlPos].SpeedDown : sign(UniCtrlList[MainCtrlPos].SpeedDown) * 0.01); // odejmuj do X - eimic += std::clamp(UniCtrlList[MainCtrlPos].SetCtrlVal - eimic, 0.0, + eimic += safe_clamp(UniCtrlList[MainCtrlPos].SetCtrlVal - eimic, 0.0, MainCtrlActualPos == MainCtrlPos ? dt * UniCtrlList[MainCtrlPos].SpeedUp : sign(UniCtrlList[MainCtrlPos].SpeedUp) * 0.01); // dodawaj do X - eimic = std::clamp(eimic, UniCtrlList[MainCtrlPos].MinCtrlVal, UniCtrlList[MainCtrlPos].MaxCtrlVal); + eimic = safe_clamp(eimic, UniCtrlList[MainCtrlPos].MinCtrlVal, UniCtrlList[MainCtrlPos].MaxCtrlVal); } if (MainCtrlActualPos == MainCtrlPos) LastRelayTime += dt; @@ -7564,13 +7564,13 @@ void TMoverParameters::CheckSpeedCtrl(double dt) // TODO: check how to disable integral part when braking in smart way // double factorI = eimicSpeedCtrlIntegral >= 0 ? SpeedCtrlUnit.FactorIpos : SpeedCtrlUnit.FactorIneg; double factorI = eimicSpeedCtrlIntegral >= 0 ? SpeedCtrlUnit.FactorIpos : SpeedCtrlUnit.FactorIneg; - eimicSpeedCtrlIntegral = std::clamp(eimicSpeedCtrlIntegral + factorI * eSCP * dt, -1.0 + eSCP, 1.0 - eSCP); + eimicSpeedCtrlIntegral = safe_clamp(eimicSpeedCtrlIntegral + factorI * eSCP * dt, -1.0 + eSCP, 1.0 - eSCP); } else { eimicSpeedCtrlIntegral = 0; } - auto const DesiredeimicSpeedCtrl{std::clamp(eimicSpeedCtrlIntegral + eSCP, -SpeedCtrlUnit.DesiredPower, accfactor)}; + auto const DesiredeimicSpeedCtrl{safe_clamp(eimicSpeedCtrlIntegral + eSCP, -SpeedCtrlUnit.DesiredPower, accfactor)}; eimicSpeedCtrl = std::clamp(DesiredeimicSpeedCtrl, eimicSpeedCtrl - SpeedCtrlUnit.PowerDownSpeed * dt, eimicSpeedCtrl + SpeedCtrlUnit.PowerUpSpeed * dt); if (Vel < SpeedCtrlUnit.FullPowerVelocity) { @@ -8183,7 +8183,7 @@ double TMoverParameters::dizel_Momentum(double dizel_fill, double n, double dt) enMoment = 0; double enrot_min = enrot - (std::min(TorqueC, TorqueL + abs(hydro_TC_TorqueIn)) - Moment) / dizel_AIM * dt; double enrot_max = enrot + (std::min(TorqueC, TorqueL + abs(hydro_TC_TorqueIn)) + Moment) / dizel_AIM * dt; - enrot = std::clamp(n, enrot_min, enrot_max); + enrot = safe_clamp(n, enrot_min, enrot_max); } if (hydro_R && hydro_R_Placement == 1) gearMoment -= dizel_MomentumRetarder(hydro_TC_nOut, dt); @@ -8564,7 +8564,7 @@ bool TMoverParameters::ChangeDoorPermitPreset(int const Change, range_t const No if (false == Doors.permit_presets.empty()) { - Doors.permit_preset = std::clamp(Doors.permit_preset + Change, 0, static_cast(Doors.permit_presets.size() - 1)); + Doors.permit_preset = safe_clamp(Doors.permit_preset + Change, 0, static_cast(Doors.permit_presets.size() - 1)); auto const doors{Doors.permit_presets[Doors.permit_preset]}; auto const permitleft{((doors & 1) != 0)}; auto const permitright{((doors & 2) != 0)}; diff --git a/utilities/utilities.h b/utilities/utilities.h index 747cac82..bc38b125 100644 --- a/utilities/utilities.h +++ b/utilities/utilities.h @@ -248,6 +248,19 @@ template bool is_equal(T const &Left, T const &Right, T const Epsil return Left == Right; } +// Tolerant clamp. Unlike std::clamp, this does NOT invoke undefined behaviour when +// the bounds are inverted (Lo > Hi) - it normalizes them instead. This restores the +// pre-refactor behaviour of the old custom clamp() for code paths where the bounds are +// computed at runtime and can legitimately cross (e.g. AI acceleration/braking limits, +// physics jolt calculations), where std::clamp's UB produced wrong results. +template +constexpr Type_ safe_clamp( Type_ const Value, Type_ const Lo, Type_ const Hi ) +{ + return ( Hi < Lo ) + ? std::clamp( Value, Hi, Lo ) + : std::clamp( Value, Lo, Hi ); +} + // keeps the provided value in specified range 0-Range, as if the range was circular buffer template T clamp_circular(T Value, T const Range = T(360)) { diff --git a/vehicle/Driver.cpp b/vehicle/Driver.cpp index 1d648a6f..328f4273 100644 --- a/vehicle/Driver.cpp +++ b/vehicle/Driver.cpp @@ -3701,7 +3701,7 @@ bool TController::IncSpeedEIM() { // TBD, TODO: set position based on desired acceleration? OK = mvControlling->MainCtrlPos < mvControlling->MainCtrlPosNo; if( OK ) { - mvControlling->MainCtrlPos = std::clamp( mvControlling->MainCtrlPos + 1, 6, mvControlling->MainCtrlPosNo ); + mvControlling->MainCtrlPos = safe_clamp( mvControlling->MainCtrlPos + 1, 6, mvControlling->MainCtrlPosNo ); } */ break; @@ -7669,7 +7669,7 @@ TController::adjust_desired_speed_for_current_speed() { if( iVehicles - ControlledEnginesCount > 0 ) { MaxAcc *= std::clamp( vel * 0.025, 0.2, 1.0 ); } - AccDesired = std::min(AccDesired, std::clamp(MaxAcc, HeavyCargoTrainAcceleration, AccPreferred)); + AccDesired = std::min(AccDesired, safe_clamp(MaxAcc, HeavyCargoTrainAcceleration, AccPreferred)); // TBD: expand this behaviour to all trains with car(s) exceeding certain weight? /* if( ( IsPassengerTrain ) && ( iVehicles - ControlledEnginesCount > 0 ) ) { @@ -7770,8 +7770,7 @@ TController::adjust_desired_speed_for_braking_test() { break; } case 3: { - auto [minV, maxV] = std::minmax(fAccThreshold * 1.01f, fAccThreshold * 1.21f); - AccDesired = std::clamp(-AbsAccS, minV, maxV); + AccDesired = safe_clamp( -AbsAccS, fAccThreshold * 1.01, fAccThreshold * 1.21 ); VelDesired = DBT_VelocityBrake; if( vel <= DBT_VelocityRelease ) { DynamicBrakeTest = 4; diff --git a/vehicle/DynObj.cpp b/vehicle/DynObj.cpp index 7adfa8b8..184d432a 100644 --- a/vehicle/DynObj.cpp +++ b/vehicle/DynObj.cpp @@ -3014,7 +3014,7 @@ TDynamicObject::update_load_offset() { 0.0 : 100.0 * MoverParameters->LoadAmount / MoverParameters->MaxLoad ) }; - LoadOffset = std::lerp( MoverParameters->LoadType.offset_min, 0.f, std::clamp( 0.0, loadpercentage * 0.01, 1.0 ) ); + LoadOffset = std::lerp( MoverParameters->LoadType.offset_min, 0.f, safe_clamp( 0.0, loadpercentage * 0.01, 1.0 ) ); } void @@ -3453,7 +3453,7 @@ bool TDynamicObject::Update(double dt, double dt1) else p->MoverParameters->MED_EPVC_CurrentTime += dt1; bool EPVC = p->MoverParameters->MED_EPVC && (p->MoverParameters->MED_EPVC_Time < 0 || p->MoverParameters->MED_EPVC_CurrentTime < p->MoverParameters->MED_EPVC_Time); - float VelC = EPVC ? std::clamp(p->MoverParameters->Vel, p->MoverParameters->MED_Vmin, p->MoverParameters->MED_Vmax) : p->MoverParameters->MED_Vref;//korekcja EP po prędkości + float VelC = EPVC ? safe_clamp(p->MoverParameters->Vel, p->MoverParameters->MED_Vmin, p->MoverParameters->MED_Vmax) : p->MoverParameters->MED_Vref;//korekcja EP po prędkości float FmaxPoj = Nmax * p->MoverParameters->Hamulec->GetFC( Nmax / (p->MoverParameters->NAxles * p->MoverParameters->NBpA), VelC) * @@ -3647,22 +3647,20 @@ bool TDynamicObject::Update(double dt, double dt1) // crude bump simulation, drop down on even axles, move back up on // the odd ones // MoverParameters->AccVert += (MoverParameters->Vel*0.1f) * - static double minV, maxV; - std::tie(minV, maxV) = std::minmax(MoverParameters->Vmax, MoverParameters->Vmax - (MoverParameters->Vel + MoverParameters->Vmax * 0.32f)); - double precalculatedValue = std::clamp(0.0, minV, maxV) * .05f * (MyTrack->iDamageFlag * 0.25f); + double const headroom = std::clamp( + MoverParameters->Vmax - ( MoverParameters->Vel + MoverParameters->Vmax * 0.32f ), + 0.0, MoverParameters->Vmax ); + double const jolt = headroom * .05f * ( MyTrack->iDamageFlag * 0.25f ); if (MyTrack->eType == tt_Normal) { - std::tie(minV, maxV) = std::minmax(4.0, precalculatedValue); - MoverParameters->AccVert += std::clamp(0.0, minV, maxV); + MoverParameters->AccVert += std::clamp( jolt, 0.0, 4.0 ); } else if (MyTrack->eType == tt_Switch) { - std::tie(minV, maxV) = std::minmax(1.0, precalculatedValue); - double accHorizontal = std::clamp(0.0, minV, maxV) * (axleindex % 2 != 0 ? 1 : -1); + double const accHorizontal = std::clamp( jolt, 0.0, 1.0 ) * ((axleindex % 2) != 0 ? 1 : -1); MoverParameters->AccS += accHorizontal; MoverParameters->AccN += accHorizontal; - std::tie(minV, maxV) = std::minmax(2.0, precalculatedValue); - MoverParameters->AccVert += std::clamp(0.0, minV, maxV); + MoverParameters->AccVert += std::clamp( jolt, 0.0, 2.0 ); } } }