From d5f1c7d329ac7856ea75d8b7dd17f5a8e7973edd Mon Sep 17 00:00:00 2001 From: FlufflesTheMicrosaur Date: Mon, 30 Mar 2026 23:10:38 -0700 Subject: [PATCH 1/2] fix: 106544: fix edgecase where lowest hp number was always rotated by a pixel due to unbounded radian angles and fp roundoff error (which might also be leading to the heap corruption warnings on painting the wrong pixels?) --- Mammoth/TSUI/CArmorHUDRingSegments.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Mammoth/TSUI/CArmorHUDRingSegments.cpp b/Mammoth/TSUI/CArmorHUDRingSegments.cpp index 33c9ab83d..58a4ab394 100644 --- a/Mammoth/TSUI/CArmorHUDRingSegments.cpp +++ b/Mammoth/TSUI/CArmorHUDRingSegments.cpp @@ -36,13 +36,15 @@ ALERROR CArmorHUDRingSegments::Bind (SDesignLoadCtx &Ctx) return NOERROR; } -void CArmorHUDRingSegments::CalcHPLabelBox (int iAngle, int iRadius, SSegment &Seg) - // CalcHPLabelBox // // Compose the position of the HP label. +// iAngle 0 is to the right +// +void CArmorHUDRingSegments::CalcHPLabelBox (int iAngle, int iRadius, SSegment &Seg) { + iAngle = AngleMod(iAngle); Metric rCenterAngle = ::mathDegreesToRadians(iAngle); // Figure out if we're drawing the text radially or along the arc. @@ -106,7 +108,7 @@ void CArmorHUDRingSegments::CalcHPLabelBox (int iAngle, int iRadius, SSegment &S { int iTextRadius = Seg.iArcRadius + ((Seg.iArcWidth - m_cyMaxValue) / 2) - 1; Seg.vHPText = CVector::FromPolarInv(rCenterAngle, iTextRadius); - Seg.rHPTextRotation = rCenterAngle + (HALF_PI); + Seg.rHPTextRotation = rCenterAngle - (HALF_PI * 3); } } } @@ -185,11 +187,12 @@ void CArmorHUDRingSegments::DrawIntegrityBox (CG32bitImage &Dest, const SSegment DEBUG_CATCH } -void CArmorHUDRingSegments::DrawIntegrityBoxText (CG32bitImage &Dest, const SSegment &Seg, CG32bitPixel rgbColor) const - // DrawIntegrityBoxText // // Draws the armor integrity value on an arc background. +// Note: Seg.rHPTextRotation should be within -pi/2 to +pi/2 +// +void CArmorHUDRingSegments::DrawIntegrityBoxText (CG32bitImage &Dest, const SSegment &Seg, CG32bitPixel rgbColor) const { DEBUG_TRY @@ -202,7 +205,7 @@ void CArmorHUDRingSegments::DrawIntegrityBoxText (CG32bitImage &Dest, const SSeg CVector vCenter(m_xCenter, m_yCenter); CVector vText = vCenter + Seg.vHPText; - if (Seg.rHPTextRotation == 0.0) + if (abs(Seg.rHPTextRotation) < g_Epsilon) MediumFont.DrawText(Dest, (int)vText.GetX(), (int)vText.GetY(), rgbColor, Seg.sHP, CG16bitFont::AlignCenter); else CGDraw::Text(Dest, From 4f39bb95896026fb142e72f03f44c21bc0f1c9b6 Mon Sep 17 00:00:00 2001 From: FlufflesTheMicrosaur Date: Wed, 1 Apr 2026 18:05:30 -0700 Subject: [PATCH 2/2] feat: 106544: additional helper functions to enable t DrawIntegrityBoxText to be more robust when detecting zero-degree cases --- Alchemy/Include/Euclid.h | 7 +++++++ Mammoth/TSUI/CArmorHUDRingSegments.cpp | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Alchemy/Include/Euclid.h b/Alchemy/Include/Euclid.h index b5e9650f3..a95db0c9d 100644 --- a/Alchemy/Include/Euclid.h +++ b/Alchemy/Include/Euclid.h @@ -15,6 +15,9 @@ constexpr Metric PI = 3.14159265358979; constexpr Metric HALF_PI = 0.5 * PI; constexpr Metric TAU = 2.0 * PI; +constexpr Metric RAD_EPSILON = 0.0000001; +constexpr Metric DEG_EPSILON = RAD_EPSILON * 180.0 / PI; + const Metric SQRT_3 = sqrt(3.0); constexpr Metric DBL_INFINITY = 1.7976931348623158e+308; // DBL_MAX @@ -61,9 +64,13 @@ inline int AngleMiddle (int iLowAngle, int iHighAngle) inline int AngleToDegrees (Metric rAngle) { return AngleMod(mathRound(rAngle * 180.0 / PI)); } inline Metric mathAngleMod (double rAngle) { if (rAngle >= 0.0) return fmod(rAngle, TAU); else return TAU - fmod(-rAngle, TAU); } +inline Metric mathAngleMod180 (double rAngle) { return mathAngleMod(rAngle + PI) - PI; } inline Metric mathAngleModDegrees (double rAngle) { if (rAngle >= 0.0) return fmod(rAngle, 360.0); else return 360.0 - fmod(-rAngle, 360.0); } +inline Metric mathAngleMod180Degrees (double rAngle) { return mathAngleModDegrees(rAngle + 180.0) - 180.0; } inline Metric mathAngleBearing (Metric rAngle, Metric rOrigin) { Metric rDiff = mathAngleMod(rAngle - rOrigin); return (rDiff > PI ? rDiff - TAU : rDiff); } inline Metric mathAngleDiff (double rFrom, double rTo) { return mathAngleMod(rTo - rFrom); } +inline Metric mathAngleIsZero (double rAngle) { return abs(mathAngleMod180(rAngle)) < RAD_EPSILON; } +inline Metric mathAngleIsZeroDegrees (double rAngle) { return abs(mathAngleMod180Degrees(rAngle)) < DEG_EPSILON; } inline constexpr Metric mathDegreesToRadians (int iAngle) { return iAngle * PI / 180.0; } inline constexpr Metric mathDegreesToRadians (Metric rDegrees) { return PI * rDegrees / 180.0; } inline constexpr Metric mathInterpolate (Metric rFrom, Metric rTo, Metric rInterpolate) { return rFrom + (rInterpolate * (rTo - rFrom)); } diff --git a/Mammoth/TSUI/CArmorHUDRingSegments.cpp b/Mammoth/TSUI/CArmorHUDRingSegments.cpp index afe9d7286..9fadeeebd 100644 --- a/Mammoth/TSUI/CArmorHUDRingSegments.cpp +++ b/Mammoth/TSUI/CArmorHUDRingSegments.cpp @@ -190,7 +190,6 @@ void CArmorHUDRingSegments::DrawIntegrityBox (CG32bitImage &Dest, const SSegment // DrawIntegrityBoxText // // Draws the armor integrity value on an arc background. -// Note: Seg.rHPTextRotation should be within -pi/2 to +pi/2 // void CArmorHUDRingSegments::DrawIntegrityBoxText (CG32bitImage &Dest, const SSegment &Seg, CG32bitPixel rgbColor) const @@ -205,7 +204,7 @@ void CArmorHUDRingSegments::DrawIntegrityBoxText (CG32bitImage &Dest, const SSeg CVector vCenter(m_xCenter, m_yCenter); CVector vText = vCenter + Seg.vHPText; - if (abs(Seg.rHPTextRotation) < g_Epsilon) + if (mathAngleIsZero(Seg.rHPTextRotation)) MediumFont.DrawText(Dest, (int)vText.GetX(), (int)vText.GetY(), rgbColor, Seg.sHP, CG16bitFont::AlignCenter); else CGDraw::Text(Dest,