NCollection_Vector_Test.cxx
OSD_Path_Test.cxx
OSD_PerfMeter_Test.cxx
+ Quantity_Color_Test.cxx
+ Quantity_ColorRGBA_Test.cxx
+ Quantity_Date_Test.cxx
+ Quantity_Period_Test.cxx
Standard_ArrayStreamBuffer_Test.cxx
Standard_Atomic_Test.cxx
Standard_Character_Test.cxx
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <Quantity_ColorRGBA.hxx>
+#include <Quantity_Color.hxx>
+
+#include <gtest/gtest.h>
+#include <cmath>
+
+// Test fixture for Quantity_ColorRGBA tests
+class Quantity_ColorRGBATest : public testing::Test
+{
+protected:
+ void SetUp() override {}
+
+ void TearDown() override {}
+
+ // Helper to compare floating point values
+ bool IsNear(Standard_ShortReal theValue1,
+ Standard_ShortReal theValue2,
+ Standard_ShortReal theTolerance = 0.001f) const
+ {
+ return std::abs(theValue1 - theValue2) < theTolerance;
+ }
+};
+
+// Test basic construction
+TEST_F(Quantity_ColorRGBATest, BasicConstruction)
+{
+ // Default constructor
+ Quantity_ColorRGBA aColor1;
+ EXPECT_TRUE(IsNear(1.0f, aColor1.GetRGB().Red())); // YELLOW = RGB(1,1,0)
+ EXPECT_TRUE(IsNear(1.0f, aColor1.GetRGB().Green())); // YELLOW = RGB(1,1,0)
+ EXPECT_TRUE(IsNear(0.0f, aColor1.GetRGB().Blue())); // YELLOW = RGB(1,1,0)
+ EXPECT_TRUE(IsNear(1.0f, aColor1.Alpha()));
+
+ // Constructor with RGB + alpha
+ Quantity_ColorRGBA aColor2(Quantity_Color(0.5, 0.6, 0.7, Quantity_TOC_RGB), 0.8f);
+ EXPECT_TRUE(IsNear(0.5f, aColor2.GetRGB().Red()));
+ EXPECT_TRUE(IsNear(0.6f, aColor2.GetRGB().Green()));
+ EXPECT_TRUE(IsNear(0.7f, aColor2.GetRGB().Blue()));
+ EXPECT_TRUE(IsNear(0.8f, aColor2.Alpha()));
+
+ // Constructor with RGBA floats
+ Quantity_ColorRGBA aColor3(0.3f, 0.4f, 0.5f, 0.6f);
+ EXPECT_TRUE(IsNear(0.3f, aColor3.GetRGB().Red()));
+ EXPECT_TRUE(IsNear(0.4f, aColor3.GetRGB().Green()));
+ EXPECT_TRUE(IsNear(0.5f, aColor3.GetRGB().Blue()));
+ EXPECT_TRUE(IsNear(0.6f, aColor3.Alpha()));
+}
+
+// Test constexpr capabilities
+TEST_F(Quantity_ColorRGBATest, ConstexprGetters)
+{
+ const Quantity_ColorRGBA aColor(0.2f, 0.4f, 0.6f, 0.8f);
+
+ // These should work with constexpr
+ Standard_ShortReal aAlpha = aColor.Alpha();
+ EXPECT_TRUE(IsNear(0.8f, aAlpha));
+}
+
+// Test SetValues
+TEST_F(Quantity_ColorRGBATest, SetValues)
+{
+ Quantity_ColorRGBA aColor;
+
+ aColor.SetValues(0.1f, 0.2f, 0.3f, 0.4f);
+ EXPECT_TRUE(IsNear(0.1f, aColor.GetRGB().Red()));
+ EXPECT_TRUE(IsNear(0.2f, aColor.GetRGB().Green()));
+ EXPECT_TRUE(IsNear(0.3f, aColor.GetRGB().Blue()));
+ EXPECT_TRUE(IsNear(0.4f, aColor.Alpha()));
+}
+
+// Test hex color parsing (tests new HEX_BASE constant)
+TEST_F(Quantity_ColorRGBATest, HexColorParsing_RGB)
+{
+ Quantity_ColorRGBA aColor;
+
+ // Test standard RGB hex format: #RRGGBB
+ bool aResult = Quantity_ColorRGBA::ColorFromHex("#FF0000", aColor, true);
+ EXPECT_TRUE(aResult);
+ EXPECT_TRUE(IsNear(1.0f, aColor.GetRGB().Red(), 0.01f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Green(), 0.01f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Blue(), 0.01f));
+
+ // Test without # prefix
+ aResult = Quantity_ColorRGBA::ColorFromHex("00FF00", aColor, true);
+ EXPECT_TRUE(aResult);
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Red(), 0.01f));
+ EXPECT_TRUE(IsNear(1.0f, aColor.GetRGB().Green(), 0.01f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Blue(), 0.01f));
+
+ // Test blue
+ aResult = Quantity_ColorRGBA::ColorFromHex("#0000FF", aColor, true);
+ EXPECT_TRUE(aResult);
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Red(), 0.01f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Green(), 0.01f));
+ EXPECT_TRUE(IsNear(1.0f, aColor.GetRGB().Blue(), 0.01f));
+}
+
+// Test RGBA hex format
+TEST_F(Quantity_ColorRGBATest, HexColorParsing_RGBA)
+{
+ Quantity_ColorRGBA aColor;
+
+ // Test RGBA hex format: #RRGGBBAA
+ bool aResult = Quantity_ColorRGBA::ColorFromHex("#FF000080", aColor, false);
+ EXPECT_TRUE(aResult);
+ EXPECT_TRUE(IsNear(1.0f, aColor.GetRGB().Red(), 0.01f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Green(), 0.01f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Blue(), 0.01f));
+ EXPECT_TRUE(IsNear(0.5f, aColor.Alpha(), 0.02f)); // 0x80 / 0xFF ~= 0.5
+}
+
+// Test short hex format (tests HEX_BITS_PER_COMPONENT_SHORT constant)
+TEST_F(Quantity_ColorRGBATest, HexColorParsing_ShortRGB)
+{
+ Quantity_ColorRGBA aColor;
+
+ // Test short RGB hex format: #RGB (each component is 0-F)
+ bool aResult = Quantity_ColorRGBA::ColorFromHex("#F00", aColor, true);
+ EXPECT_TRUE(aResult);
+ EXPECT_TRUE(IsNear(1.0f, aColor.GetRGB().Red(), 0.1f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Green(), 0.1f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Blue(), 0.1f));
+
+ // Test short format for white
+ aResult = Quantity_ColorRGBA::ColorFromHex("#FFF", aColor, true);
+ EXPECT_TRUE(aResult);
+ EXPECT_TRUE(IsNear(1.0f, aColor.GetRGB().Red(), 0.1f));
+ EXPECT_TRUE(IsNear(1.0f, aColor.GetRGB().Green(), 0.1f));
+ EXPECT_TRUE(IsNear(1.0f, aColor.GetRGB().Blue(), 0.1f));
+}
+
+// Test short RGBA hex format
+TEST_F(Quantity_ColorRGBATest, HexColorParsing_ShortRGBA)
+{
+ Quantity_ColorRGBA aColor;
+
+ // Test short RGBA hex format: #RGBA
+ bool aResult = Quantity_ColorRGBA::ColorFromHex("#F008", aColor, false);
+ EXPECT_TRUE(aResult);
+ EXPECT_TRUE(IsNear(1.0f, aColor.GetRGB().Red(), 0.1f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Green(), 0.1f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Blue(), 0.1f));
+ EXPECT_TRUE(IsNear(0.5f, aColor.Alpha(), 0.1f)); // 0x8 / 0xF ~= 0.533
+}
+
+// Test invalid hex formats
+TEST_F(Quantity_ColorRGBATest, HexColorParsing_Invalid)
+{
+ Quantity_ColorRGBA aColor;
+
+ // Empty string
+ EXPECT_FALSE(Quantity_ColorRGBA::ColorFromHex("", aColor, true));
+
+ // Invalid length
+ EXPECT_FALSE(Quantity_ColorRGBA::ColorFromHex("#FFFF", aColor, true));
+
+ // Invalid characters
+ EXPECT_FALSE(Quantity_ColorRGBA::ColorFromHex("#GGGGGG", aColor, true));
+
+ // RGBA format when alpha is disabled
+ EXPECT_FALSE(Quantity_ColorRGBA::ColorFromHex("#FF0000FF", aColor, true));
+}
+
+// Test mixed case hex parsing
+TEST_F(Quantity_ColorRGBATest, HexColorParsing_MixedCase)
+{
+ Quantity_ColorRGBA aColor;
+
+ bool aResult = Quantity_ColorRGBA::ColorFromHex("#FfAa00", aColor, true);
+ EXPECT_TRUE(aResult);
+ EXPECT_TRUE(IsNear(1.0f, aColor.GetRGB().Red(), 0.01f));
+ // AA = 170/255 = 0.667 sRGB, converts to ~0.402 linear RGB
+ EXPECT_TRUE(IsNear(0.402f, aColor.GetRGB().Green(), 0.01f));
+ EXPECT_TRUE(IsNear(0.0f, aColor.GetRGB().Blue(), 0.01f));
+}
+
+// Test specific hex value from our constant migration (regression test)
+TEST_F(Quantity_ColorRGBATest, HexColorParsing_SpecificValues)
+{
+ Quantity_ColorRGBA aColor;
+
+ // Test a color that would use our HEX_BASE constant (16)
+ bool aResult = Quantity_ColorRGBA::ColorFromHex("#102030", aColor, true);
+ EXPECT_TRUE(aResult);
+
+ // Hex values are sRGB that get converted to linear RGB
+ // 0x10 = 16/255 = 0.0627 sRGB -> 0.00518 linear RGB
+ // 0x20 = 32/255 = 0.1255 sRGB -> 0.01444 linear RGB
+ // 0x30 = 48/255 = 0.1882 sRGB -> 0.02956 linear RGB
+ EXPECT_TRUE(IsNear(0.00518f, aColor.GetRGB().Red(), 0.0001f));
+ EXPECT_TRUE(IsNear(0.01444f, aColor.GetRGB().Green(), 0.0001f));
+ EXPECT_TRUE(IsNear(0.02956f, aColor.GetRGB().Blue(), 0.0001f));
+}
+
+// Test equality comparison
+TEST_F(Quantity_ColorRGBATest, EqualityComparison)
+{
+ Quantity_ColorRGBA aColor1(0.5f, 0.6f, 0.7f, 0.8f);
+ Quantity_ColorRGBA aColor2(0.5f, 0.6f, 0.7f, 0.8f);
+ Quantity_ColorRGBA aColor3(0.5f, 0.6f, 0.7f, 0.9f); // Different alpha
+
+ EXPECT_TRUE(aColor1.IsEqual(aColor2));
+ EXPECT_FALSE(aColor1.IsEqual(aColor3));
+}
+
+// Test GetRGB and ChangeRGB
+TEST_F(Quantity_ColorRGBATest, RGBAccess)
+{
+ Quantity_ColorRGBA aColor(0.2f, 0.4f, 0.6f, 0.8f);
+
+ const Quantity_Color& aRGB = aColor.GetRGB();
+ EXPECT_TRUE(IsNear(0.2f, aRGB.Red()));
+ EXPECT_TRUE(IsNear(0.4f, aRGB.Green()));
+ EXPECT_TRUE(IsNear(0.6f, aRGB.Blue()));
+
+ aColor.ChangeRGB().SetValues(0.3f, 0.5f, 0.7f, Quantity_TOC_RGB);
+ EXPECT_TRUE(IsNear(0.3f, aColor.GetRGB().Red()));
+ EXPECT_TRUE(IsNear(0.5f, aColor.GetRGB().Green()));
+ EXPECT_TRUE(IsNear(0.7f, aColor.GetRGB().Blue()));
+ EXPECT_TRUE(IsNear(0.8f, aColor.Alpha())); // Alpha unchanged
+}
+
+// Test SetAlpha
+TEST_F(Quantity_ColorRGBATest, SetAlpha)
+{
+ Quantity_ColorRGBA aColor(0.5f, 0.5f, 0.5f, 1.0f);
+ EXPECT_TRUE(IsNear(1.0f, aColor.Alpha()));
+
+ aColor.SetAlpha(0.5f);
+ EXPECT_TRUE(IsNear(0.5f, aColor.Alpha()));
+
+ // RGB should be unchanged
+ EXPECT_TRUE(IsNear(0.5f, aColor.GetRGB().Red()));
+ EXPECT_TRUE(IsNear(0.5f, aColor.GetRGB().Green()));
+ EXPECT_TRUE(IsNear(0.5f, aColor.GetRGB().Blue()));
+}
+
+// Test edge cases
+TEST_F(Quantity_ColorRGBATest, EdgeCases)
+{
+ // Fully transparent black
+ Quantity_ColorRGBA aTransparent(0.0f, 0.0f, 0.0f, 0.0f);
+ EXPECT_TRUE(IsNear(0.0f, aTransparent.Alpha()));
+
+ // Fully opaque white
+ Quantity_ColorRGBA aOpaque(1.0f, 1.0f, 1.0f, 1.0f);
+ EXPECT_TRUE(IsNear(1.0f, aOpaque.Alpha()));
+ EXPECT_TRUE(IsNear(1.0f, aOpaque.GetRGB().Red()));
+}
+
+// Test color component extraction in correct order (RGB_COMPONENT_LAST_INDEX)
+TEST_F(Quantity_ColorRGBATest, ComponentOrder)
+{
+ Quantity_ColorRGBA aColor;
+
+ // Parse a color where each component is distinct
+ bool aResult = Quantity_ColorRGBA::ColorFromHex("#123456", aColor, true);
+ EXPECT_TRUE(aResult);
+
+ // Verify the order is correct: R=0x12, G=0x34, B=0x56
+ // Hex values are sRGB that get converted to linear RGB
+ // 0x12 = 18/255 = 0.0706 sRGB -> 0.00605 linear RGB
+ // 0x34 = 52/255 = 0.2039 sRGB -> 0.03434 linear RGB
+ // 0x56 = 86/255 = 0.3373 sRGB -> 0.09306 linear RGB
+ float aExpectedR = 0.00605f;
+ float aExpectedG = 0.03434f;
+ float aExpectedB = 0.09306f;
+
+ EXPECT_TRUE(IsNear(aExpectedR, aColor.GetRGB().Red(), 0.0001f));
+ EXPECT_TRUE(IsNear(aExpectedG, aColor.GetRGB().Green(), 0.0001f));
+ EXPECT_TRUE(IsNear(aExpectedB, aColor.GetRGB().Blue(), 0.0001f));
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <Quantity_Color.hxx>
+#include <Quantity_NameOfColor.hxx>
+
+#include <gtest/gtest.h>
+#include <cmath>
+
+// Test fixture for Quantity_Color tests
+class Quantity_ColorTest : public testing::Test
+{
+protected:
+ void SetUp() override {}
+
+ void TearDown() override {}
+
+ // Helper to compare floating point values
+ bool IsNear(Standard_Real theValue1,
+ Standard_Real theValue2,
+ Standard_Real theTolerance = 0.001) const
+ {
+ return std::abs(theValue1 - theValue2) < theTolerance;
+ }
+};
+
+// Test basic construction
+TEST_F(Quantity_ColorTest, BasicConstruction)
+{
+ // Default constructor
+ Quantity_Color aColor1;
+ EXPECT_TRUE(IsNear(1.0, aColor1.Red())); // YELLOW = RGB(1,1,0)
+ EXPECT_TRUE(IsNear(1.0, aColor1.Green())); // YELLOW = RGB(1,1,0)
+ EXPECT_TRUE(IsNear(0.0, aColor1.Blue())); // YELLOW = RGB(1,1,0)
+
+ // RGB constructor
+ Quantity_Color aColor2(0.5, 0.6, 0.7, Quantity_TOC_RGB);
+ EXPECT_TRUE(IsNear(0.5, aColor2.Red()));
+ EXPECT_TRUE(IsNear(0.6, aColor2.Green()));
+ EXPECT_TRUE(IsNear(0.7, aColor2.Blue()));
+
+ // Named color constructor
+ Quantity_Color aColor3(Quantity_NOC_RED);
+ EXPECT_TRUE(IsNear(1.0, aColor3.Red()));
+ EXPECT_TRUE(IsNear(0.0, aColor3.Green()));
+ EXPECT_TRUE(IsNear(0.0, aColor3.Blue()));
+}
+
+// Test constexpr getters (compile-time evaluation capability)
+TEST_F(Quantity_ColorTest, ConstexprGetters)
+{
+ const Quantity_Color aColor(0.3, 0.5, 0.7, Quantity_TOC_RGB);
+
+ // These should work at compile-time with constexpr
+ Standard_Real aR = aColor.Red();
+ Standard_Real aG = aColor.Green();
+ Standard_Real aB = aColor.Blue();
+
+ EXPECT_TRUE(IsNear(0.3, aR));
+ EXPECT_TRUE(IsNear(0.5, aG));
+ EXPECT_TRUE(IsNear(0.7, aB));
+}
+
+// Test equality comparison (noexcept guarantee)
+TEST_F(Quantity_ColorTest, EqualityComparison)
+{
+ Quantity_Color aColor1(0.5, 0.6, 0.7, Quantity_TOC_RGB);
+ Quantity_Color aColor2(0.5, 0.6, 0.7, Quantity_TOC_RGB);
+ Quantity_Color aColor3(0.5, 0.6, 0.8, Quantity_TOC_RGB);
+
+ EXPECT_TRUE(aColor1.IsEqual(aColor2));
+ EXPECT_TRUE(aColor1 == aColor2);
+ EXPECT_FALSE(aColor1.IsDifferent(aColor2));
+ EXPECT_FALSE(aColor1 != aColor2);
+
+ EXPECT_FALSE(aColor1.IsEqual(aColor3));
+ EXPECT_FALSE(aColor1 == aColor3);
+ EXPECT_TRUE(aColor1.IsDifferent(aColor3));
+ EXPECT_TRUE(aColor1 != aColor3);
+}
+
+// Test distance calculation (noexcept guarantee)
+TEST_F(Quantity_ColorTest, DistanceCalculation)
+{
+ Quantity_Color aColor1(0.0, 0.0, 0.0, Quantity_TOC_RGB);
+ Quantity_Color aColor2(0.3, 0.4, 0.0, Quantity_TOC_RGB);
+
+ // Distance should be sqrt(0.3^2 + 0.4^2) = sqrt(0.09 + 0.16) = sqrt(0.25) = 0.5
+ Standard_Real aDist = aColor1.Distance(aColor2);
+ EXPECT_TRUE(IsNear(0.5, aDist));
+
+ Standard_Real aSquareDist = aColor1.SquareDistance(aColor2);
+ EXPECT_TRUE(IsNear(0.25, aSquareDist));
+}
+
+// Test sRGB to HLS conversion
+TEST_F(Quantity_ColorTest, RGB_to_HLS_Conversion)
+{
+ // Pure red in sRGB
+ Quantity_Color aRed(Quantity_NOC_RED);
+ NCollection_Vec3<float> aHLS = Quantity_Color::Convert_sRGB_To_HLS(aRed.Rgb());
+
+ EXPECT_TRUE(IsNear(0.0, aHLS[0], 1.0)); // Hue for red should be ~0
+ EXPECT_TRUE(IsNear(1.0, aHLS[1])); // Lightness should be 1 (max value)
+ EXPECT_TRUE(IsNear(1.0, aHLS[2])); // Saturation should be 1 (fully saturated)
+
+ // Gray (no saturation)
+ Quantity_Color aGray(0.5, 0.5, 0.5, Quantity_TOC_RGB);
+ NCollection_Vec3<float> aHLS_Gray = Quantity_Color::Convert_sRGB_To_HLS(aGray.Rgb());
+
+ EXPECT_TRUE(IsNear(0.5, aHLS_Gray[1])); // Lightness
+ EXPECT_TRUE(IsNear(0.0, aHLS_Gray[2])); // Saturation should be 0 for gray
+}
+
+// Test Linear RGB to CIE Lab conversion (uses new constexpr constants)
+TEST_F(Quantity_ColorTest, LinearRGB_to_Lab_Conversion)
+{
+ // White should convert to L=100, a=0, b=0 in Lab
+ Quantity_Color aWhite(1.0, 1.0, 1.0, Quantity_TOC_RGB);
+ NCollection_Vec3<float> aLab = Quantity_Color::Convert_LinearRGB_To_Lab(aWhite.Rgb());
+
+ EXPECT_TRUE(IsNear(100.0, aLab[0], 1.0)); // L should be near 100
+ EXPECT_TRUE(IsNear(0.0, aLab[1], 5.0)); // a should be near 0
+ EXPECT_TRUE(IsNear(0.0, aLab[2], 5.0)); // b should be near 0
+
+ // Black should convert to L=0
+ Quantity_Color aBlack(0.0, 0.0, 0.0, Quantity_TOC_RGB);
+ NCollection_Vec3<float> aLabBlack = Quantity_Color::Convert_LinearRGB_To_Lab(aBlack.Rgb());
+
+ EXPECT_TRUE(IsNear(0.0, aLabBlack[0], 1.0)); // L should be 0
+}
+
+// Test Lab to Lch conversion
+TEST_F(Quantity_ColorTest, Lab_to_Lch_Conversion)
+{
+ // Test with known Lab values
+ NCollection_Vec3<float> aLab(50.0f, 25.0f, 25.0f);
+ NCollection_Vec3<float> aLch = Quantity_Color::Convert_Lab_To_Lch(aLab);
+
+ EXPECT_TRUE(IsNear(50.0, aLch[0])); // L should be preserved
+
+ // C (chroma) should be sqrt(25^2 + 25^2) = sqrt(1250) ~= 35.36
+ EXPECT_TRUE(IsNear(35.36, aLch[1], 0.1));
+
+ // H (hue) should be atan2(25, 25) * 180/pi = 45 degrees
+ EXPECT_TRUE(IsNear(45.0, aLch[2], 1.0));
+}
+
+// Test Lch to Lab conversion (round-trip)
+TEST_F(Quantity_ColorTest, Lch_to_Lab_RoundTrip)
+{
+ NCollection_Vec3<float> aLab1(50.0f, 25.0f, 25.0f);
+ NCollection_Vec3<float> aLch = Quantity_Color::Convert_Lab_To_Lch(aLab1);
+ NCollection_Vec3<float> aLab2 = Quantity_Color::Convert_Lch_To_Lab(aLch);
+
+ EXPECT_TRUE(IsNear(aLab1[0], aLab2[0], 0.01));
+ EXPECT_TRUE(IsNear(aLab1[1], aLab2[1], 0.01));
+ EXPECT_TRUE(IsNear(aLab1[2], aLab2[2], 0.01));
+}
+
+// Test Lab to RGB conversion (round-trip validation)
+TEST_F(Quantity_ColorTest, Lab_to_RGB_RoundTrip)
+{
+ Quantity_Color aOriginal(0.5, 0.6, 0.7, Quantity_TOC_RGB);
+ NCollection_Vec3<float> aLab = Quantity_Color::Convert_LinearRGB_To_Lab(aOriginal.Rgb());
+ NCollection_Vec3<float> aRGB = Quantity_Color::Convert_Lab_To_LinearRGB(aLab);
+
+ EXPECT_TRUE(IsNear(aOriginal.Red(), aRGB[0], 0.01));
+ EXPECT_TRUE(IsNear(aOriginal.Green(), aRGB[1], 0.01));
+ EXPECT_TRUE(IsNear(aOriginal.Blue(), aRGB[2], 0.01));
+}
+
+// Test DeltaE2000 color difference (uses Epsilon() function - regression test for bug fix)
+TEST_F(Quantity_ColorTest, DeltaE2000_Calculation)
+{
+ // Same color should have DeltaE = 0
+ Quantity_Color aColor1(0.5, 0.6, 0.7, Quantity_TOC_RGB);
+ Quantity_Color aColor2(0.5, 0.6, 0.7, Quantity_TOC_RGB);
+
+ Standard_Real aDeltaE = aColor1.DeltaE2000(aColor2);
+ EXPECT_TRUE(IsNear(0.0, aDeltaE, 0.01));
+
+ // Different colors should have non-zero DeltaE
+ Quantity_Color aColor3(0.3, 0.4, 0.5, Quantity_TOC_RGB);
+ Standard_Real aDeltaE2 = aColor1.DeltaE2000(aColor3);
+ EXPECT_GT(aDeltaE2, 0.0);
+}
+
+// Test named color conversion
+TEST_F(Quantity_ColorTest, NamedColors)
+{
+ // Test a few standard colors
+ Quantity_Color aRed(Quantity_NOC_RED);
+ EXPECT_TRUE(IsNear(1.0, aRed.Red()));
+ EXPECT_TRUE(IsNear(0.0, aRed.Green()));
+ EXPECT_TRUE(IsNear(0.0, aRed.Blue()));
+
+ Quantity_Color aGreen(Quantity_NOC_GREEN);
+ EXPECT_TRUE(IsNear(0.0, aGreen.Red()));
+ EXPECT_GT(aGreen.Green(), 0.5); // Green should be significant
+ EXPECT_TRUE(IsNear(0.0, aGreen.Blue()));
+
+ Quantity_Color aBlue(Quantity_NOC_BLUE);
+ EXPECT_TRUE(IsNear(0.0, aBlue.Red()));
+ EXPECT_TRUE(IsNear(0.0, aBlue.Green()));
+ EXPECT_TRUE(IsNear(1.0, aBlue.Blue()));
+}
+
+// Test SetValues and modification
+TEST_F(Quantity_ColorTest, SetValues)
+{
+ Quantity_Color aColor;
+
+ aColor.SetValues(0.2, 0.4, 0.6, Quantity_TOC_RGB);
+ EXPECT_TRUE(IsNear(0.2, aColor.Red()));
+ EXPECT_TRUE(IsNear(0.4, aColor.Green()));
+ EXPECT_TRUE(IsNear(0.6, aColor.Blue()));
+
+ aColor.SetValues(Quantity_NOC_YELLOW);
+ EXPECT_TRUE(IsNear(1.0, aColor.Red()));
+ EXPECT_TRUE(IsNear(1.0, aColor.Green()));
+ EXPECT_TRUE(IsNear(0.0, aColor.Blue()));
+}
+
+// Test HLS values extraction
+TEST_F(Quantity_ColorTest, HLS_Extraction)
+{
+ Quantity_Color aRed(Quantity_NOC_RED);
+
+ // For pure red, hue should be ~0, saturation should be 1, lightness should be 1
+ Standard_Real aHue = aRed.Hue();
+ Standard_Real aLight = aRed.Light();
+ Standard_Real aSat = aRed.Saturation();
+
+ EXPECT_TRUE(IsNear(0.0, aHue, 5.0) || IsNear(360.0, aHue, 5.0)); // Hue wraps around
+ EXPECT_TRUE(IsNear(1.0, aLight, 0.01));
+ EXPECT_TRUE(IsNear(1.0, aSat, 0.01));
+}
+
+// Test thread-safety of Epsilon getter/setter
+TEST_F(Quantity_ColorTest, EpsilonThreadSafety)
+{
+ Standard_Real aOriginalEpsilon = Quantity_Color::Epsilon();
+
+ // Set new epsilon
+ Quantity_Color::SetEpsilon(0.0002);
+ EXPECT_TRUE(IsNear(0.0002, Quantity_Color::Epsilon()));
+
+ // Restore original
+ Quantity_Color::SetEpsilon(aOriginalEpsilon);
+ EXPECT_TRUE(IsNear(aOriginalEpsilon, Quantity_Color::Epsilon()));
+}
+
+// Test color name string conversion
+TEST_F(Quantity_ColorTest, ColorNameString)
+{
+ Standard_CString aRedName = Quantity_Color::StringName(Quantity_NOC_RED);
+ EXPECT_STREQ("RED", aRedName);
+
+ Standard_CString aBlueName = Quantity_Color::StringName(Quantity_NOC_BLUE);
+ EXPECT_STREQ("BLUE", aBlueName);
+}
+
+// Test edge cases and boundary conditions
+TEST_F(Quantity_ColorTest, EdgeCases)
+{
+ // Test with zero values
+ Quantity_Color aBlack(0.0, 0.0, 0.0, Quantity_TOC_RGB);
+ EXPECT_TRUE(IsNear(0.0, aBlack.Red()));
+ EXPECT_TRUE(IsNear(0.0, aBlack.Green()));
+ EXPECT_TRUE(IsNear(0.0, aBlack.Blue()));
+
+ // Test with max values
+ Quantity_Color aWhite(1.0, 1.0, 1.0, Quantity_TOC_RGB);
+ EXPECT_TRUE(IsNear(1.0, aWhite.Red()));
+ EXPECT_TRUE(IsNear(1.0, aWhite.Green()));
+ EXPECT_TRUE(IsNear(1.0, aWhite.Blue()));
+
+ // Test equality with epsilon tolerance
+ Quantity_Color aColor1(0.5, 0.5, 0.5, Quantity_TOC_RGB);
+ Quantity_Color aColor2(0.50001, 0.50001, 0.50001, Quantity_TOC_RGB);
+ EXPECT_TRUE(aColor1.IsEqual(aColor2)); // Should be equal within epsilon
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <Quantity_Date.hxx>
+#include <Quantity_Period.hxx>
+
+#include <gtest/gtest.h>
+
+// Test fixture for Quantity_Date tests
+class Quantity_DateTest : public testing::Test
+{
+protected:
+ void SetUp() override {}
+
+ void TearDown() override {}
+};
+
+// Test basic construction and default initialization
+TEST_F(Quantity_DateTest, BasicConstruction)
+{
+ // Default constructor creates January 1, 1979, 00:00:00
+ Quantity_Date aDate1;
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ aDate1.Values(mm, dd, yy, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(1, mm); // January
+ EXPECT_EQ(1, dd); // 1st
+ EXPECT_EQ(1979, yy); // 1979
+ EXPECT_EQ(0, hh); // 00 hours
+ EXPECT_EQ(0, mn); // 00 minutes
+ EXPECT_EQ(0, ss); // 00 seconds
+
+ // Parameterized constructor
+ Quantity_Date aDate2(6, 15, 2025, 14, 30, 45, 123, 456);
+ aDate2.Values(mm, dd, yy, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(6, mm);
+ EXPECT_EQ(15, dd);
+ EXPECT_EQ(2025, yy);
+ EXPECT_EQ(14, hh);
+ EXPECT_EQ(30, mn);
+ EXPECT_EQ(45, ss);
+ EXPECT_EQ(123, mis);
+ EXPECT_EQ(456, mics);
+}
+
+// Test constexpr comparison operators
+TEST_F(Quantity_DateTest, ConstexprComparisons)
+{
+ Quantity_Date aDate1(1, 1, 2020, 0, 0, 0, 0, 0);
+ Quantity_Date aDate2(1, 1, 2020, 0, 0, 0, 0, 0);
+ Quantity_Date aDate3(1, 2, 2020, 0, 0, 0, 0, 0);
+
+ // Test IsEqual (constexpr)
+ EXPECT_TRUE(aDate1.IsEqual(aDate2));
+ EXPECT_FALSE(aDate1.IsEqual(aDate3));
+
+ // Test IsEarlier (constexpr)
+ EXPECT_TRUE(aDate1.IsEarlier(aDate3));
+ EXPECT_FALSE(aDate3.IsEarlier(aDate1));
+ EXPECT_FALSE(aDate1.IsEarlier(aDate2));
+
+ // Test IsLater (constexpr)
+ EXPECT_TRUE(aDate3.IsLater(aDate1));
+ EXPECT_FALSE(aDate1.IsLater(aDate3));
+ EXPECT_FALSE(aDate1.IsLater(aDate2));
+}
+
+// Test leap year detection (constexpr)
+TEST_F(Quantity_DateTest, LeapYearDetection)
+{
+ // Leap years
+ EXPECT_TRUE(Quantity_Date::IsLeap(2000)); // Divisible by 400
+ EXPECT_TRUE(Quantity_Date::IsLeap(2004)); // Divisible by 4, not by 100
+ EXPECT_TRUE(Quantity_Date::IsLeap(2020));
+
+ // Non-leap years
+ EXPECT_FALSE(Quantity_Date::IsLeap(1900)); // Divisible by 100 but not 400
+ EXPECT_FALSE(Quantity_Date::IsLeap(2001)); // Not divisible by 4
+ EXPECT_FALSE(Quantity_Date::IsLeap(2100)); // Divisible by 100 but not 400
+}
+
+// Test validation with leap years (thread-safety regression test)
+TEST_F(Quantity_DateTest, ValidationWithLeapYear)
+{
+ // Valid date in leap year (February 29)
+ EXPECT_TRUE(Quantity_Date::IsValid(2, 29, 2020, 0, 0, 0, 0, 0));
+
+ // Invalid date in non-leap year (February 29)
+ EXPECT_FALSE(Quantity_Date::IsValid(2, 29, 2019, 0, 0, 0, 0, 0));
+
+ // Valid February 28 in any year
+ EXPECT_TRUE(Quantity_Date::IsValid(2, 28, 2019, 0, 0, 0, 0, 0));
+ EXPECT_TRUE(Quantity_Date::IsValid(2, 28, 2020, 0, 0, 0, 0, 0));
+}
+
+// Test month boundary validation (uses constexpr getDaysInMonth)
+TEST_F(Quantity_DateTest, MonthBoundaries)
+{
+ // January has 31 days
+ EXPECT_TRUE(Quantity_Date::IsValid(1, 31, 2020, 0, 0, 0, 0, 0));
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 32, 2020, 0, 0, 0, 0, 0));
+
+ // April has 30 days
+ EXPECT_TRUE(Quantity_Date::IsValid(4, 30, 2020, 0, 0, 0, 0, 0));
+ EXPECT_FALSE(Quantity_Date::IsValid(4, 31, 2020, 0, 0, 0, 0, 0));
+
+ // December has 31 days
+ EXPECT_TRUE(Quantity_Date::IsValid(12, 31, 2020, 0, 0, 0, 0, 0));
+ EXPECT_FALSE(Quantity_Date::IsValid(12, 32, 2020, 0, 0, 0, 0, 0));
+}
+
+// Test time validation (uses time constants)
+TEST_F(Quantity_DateTest, TimeValidation)
+{
+ // Valid times
+ EXPECT_TRUE(Quantity_Date::IsValid(1, 1, 2020, 23, 59, 59, 999, 999));
+ EXPECT_TRUE(Quantity_Date::IsValid(1, 1, 2020, 0, 0, 0, 0, 0));
+
+ // Invalid hours
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 1, 2020, 24, 0, 0, 0, 0));
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 1, 2020, -1, 0, 0, 0, 0));
+
+ // Invalid minutes
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 1, 2020, 0, 60, 0, 0, 0));
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 1, 2020, 0, -1, 0, 0, 0));
+
+ // Invalid seconds
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 1, 2020, 0, 0, 60, 0, 0));
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 1, 2020, 0, 0, -1, 0, 0));
+
+ // Invalid milliseconds
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 1, 2020, 0, 0, 0, 1000, 0));
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 1, 2020, 0, 0, 0, -1, 0));
+
+ // Invalid microseconds
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 1, 2020, 0, 0, 0, 0, 1000));
+ EXPECT_FALSE(Quantity_Date::IsValid(1, 1, 2020, 0, 0, 0, 0, -1));
+}
+
+// Test SetValues and Values round-trip (tests SECONDS_PER_* constants)
+TEST_F(Quantity_DateTest, SetValuesRoundTrip)
+{
+ Quantity_Date aDate;
+ aDate.SetValues(3, 15, 2021, 10, 25, 30, 500, 750);
+
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ aDate.Values(mm, dd, yy, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(3, mm);
+ EXPECT_EQ(15, dd);
+ EXPECT_EQ(2021, yy);
+ EXPECT_EQ(10, hh);
+ EXPECT_EQ(25, mn);
+ EXPECT_EQ(30, ss);
+ EXPECT_EQ(500, mis);
+ EXPECT_EQ(750, mics);
+}
+
+// Test individual component getters
+TEST_F(Quantity_DateTest, IndividualGetters)
+{
+ // Test with a simple date first: January 2, 1979
+ Quantity_Date aDate1(1, 2, 1979, 0, 0, 0, 0, 0);
+ EXPECT_EQ(1, aDate1.Month());
+ EXPECT_EQ(2, aDate1.Day());
+ EXPECT_EQ(1979, aDate1.Year());
+
+ // Test with July 20, 2024 at midnight
+ Quantity_Date aDate2(7, 20, 2024, 0, 0, 0, 0, 0);
+ EXPECT_EQ(7, aDate2.Month());
+ EXPECT_EQ(20, aDate2.Day());
+ EXPECT_EQ(2024, aDate2.Year());
+
+ // Test with July 20, 2024 with time components
+ Quantity_Date aDate3(7, 20, 2024, 15, 45, 30, 123, 456);
+ EXPECT_EQ(7, aDate3.Month());
+ EXPECT_EQ(20, aDate3.Day());
+ EXPECT_EQ(2024, aDate3.Year());
+ EXPECT_EQ(15, aDate3.Hour());
+ EXPECT_EQ(45, aDate3.Minute());
+ EXPECT_EQ(30, aDate3.Second());
+ EXPECT_EQ(123, aDate3.MilliSecond());
+ EXPECT_EQ(456, aDate3.MicroSecond());
+}
+
+// Test date difference calculation (uses USECS_PER_SEC constant)
+TEST_F(Quantity_DateTest, DateDifference)
+{
+ Quantity_Date aDate1(1, 1, 2020, 0, 0, 0, 0, 0);
+ Quantity_Date aDate2(1, 1, 2020, 1, 0, 0, 0, 0); // 1 hour later
+
+ Quantity_Period aPeriod = aDate2.Difference(aDate1);
+
+ Standard_Integer ss, mics;
+ aPeriod.Values(ss, mics);
+
+ EXPECT_EQ(3600, ss); // 1 hour = 3600 seconds (SECONDS_PER_HOUR)
+ EXPECT_EQ(0, mics);
+}
+
+// Test adding period to date (uses USECS_PER_SEC constant)
+TEST_F(Quantity_DateTest, AddPeriod)
+{
+ Quantity_Date aDate(1, 1, 2020, 0, 0, 0, 0, 0);
+ Quantity_Period aPeriod(1, 0, 0, 0, 0, 0); // 1 day
+
+ Quantity_Date aNewDate = aDate.Add(aPeriod);
+
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ aNewDate.Values(mm, dd, yy, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(1, mm);
+ EXPECT_EQ(2, dd); // Next day
+ EXPECT_EQ(2020, yy);
+}
+
+// Test subtracting period from date
+TEST_F(Quantity_DateTest, SubtractPeriod)
+{
+ Quantity_Date aDate(1, 2, 2020, 0, 0, 0, 0, 0); // January 2
+ Quantity_Period aPeriod(1, 0, 0, 0, 0, 0); // 1 day
+
+ Quantity_Date aNewDate = aDate.Subtract(aPeriod);
+
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ aNewDate.Values(mm, dd, yy, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(1, mm);
+ EXPECT_EQ(1, dd); // Previous day
+ EXPECT_EQ(2020, yy);
+}
+
+// Test year boundary crossing
+TEST_F(Quantity_DateTest, YearBoundary)
+{
+ Quantity_Date aDate(12, 31, 2020, 23, 59, 59, 0, 0);
+ Quantity_Period aPeriod(0, 0, 0, 1, 0, 0); // 1 second
+
+ Quantity_Date aNewDate = aDate.Add(aPeriod);
+
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ aNewDate.Values(mm, dd, yy, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(1, mm); // January
+ EXPECT_EQ(1, dd); // 1st
+ EXPECT_EQ(2021, yy); // Next year
+ EXPECT_EQ(0, hh);
+ EXPECT_EQ(0, mn);
+ EXPECT_EQ(0, ss);
+}
+
+// Test leap year boundary (Feb 28/29)
+TEST_F(Quantity_DateTest, LeapYearBoundary)
+{
+ // Leap year: Feb 28 + 1 day = Feb 29
+ Quantity_Date aDate1(2, 28, 2020, 0, 0, 0, 0, 0);
+ Quantity_Period aPeriod1(1, 0, 0, 0, 0, 0);
+ Quantity_Date aNewDate1 = aDate1.Add(aPeriod1);
+
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ aNewDate1.Values(mm, dd, yy, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(2, mm);
+ EXPECT_EQ(29, dd); // Feb 29 exists in 2020
+ EXPECT_EQ(2020, yy);
+
+ // Non-leap year: Feb 28 + 1 day = Mar 1
+ Quantity_Date aDate2(2, 28, 2021, 0, 0, 0, 0, 0);
+ Quantity_Period aPeriod2(1, 0, 0, 0, 0, 0);
+ Quantity_Date aNewDate2 = aDate2.Add(aPeriod2);
+
+ aNewDate2.Values(mm, dd, yy, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(3, mm); // March
+ EXPECT_EQ(1, dd); // 1st
+ EXPECT_EQ(2021, yy);
+}
+
+// Test microsecond overflow (uses USECS_PER_SEC constant)
+TEST_F(Quantity_DateTest, MicrosecondOverflow)
+{
+ Quantity_Date aDate(1, 1, 2020, 0, 0, 0, 999, 999);
+ Quantity_Period aPeriod(0, 0, 0, 0, 0, 1); // 1 microsecond
+
+ Quantity_Date aNewDate = aDate.Add(aPeriod);
+
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ aNewDate.Values(mm, dd, yy, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(1, mm);
+ EXPECT_EQ(1, dd);
+ EXPECT_EQ(2020, yy);
+ EXPECT_EQ(0, hh);
+ EXPECT_EQ(0, mn);
+ EXPECT_EQ(1, ss); // Should overflow to next second
+ EXPECT_EQ(0, mis);
+ EXPECT_EQ(0, mics);
+}
+
+// Test specific date calculations (regression test for time constants)
+TEST_F(Quantity_DateTest, SpecificDateCalculations)
+{
+ // Test that 24 hours = 1 day (SECONDS_PER_DAY = 86400)
+ Quantity_Date aDate1(1, 1, 2020, 0, 0, 0, 0, 0);
+ Quantity_Period aPeriod24h(0, 24, 0, 0, 0, 0); // 24 hours
+
+ Quantity_Date aDate2 = aDate1.Add(aPeriod24h);
+
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ aDate2.Values(mm, dd, yy, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(1, mm);
+ EXPECT_EQ(2, dd); // Next day
+ EXPECT_EQ(2020, yy);
+ EXPECT_EQ(0, hh);
+}
+
+// Test minimum date (January 1, 1979)
+TEST_F(Quantity_DateTest, MinimumDate)
+{
+ EXPECT_TRUE(Quantity_Date::IsValid(1, 1, 1979, 0, 0, 0, 0, 0));
+ EXPECT_FALSE(Quantity_Date::IsValid(12, 31, 1978, 23, 59, 59, 999, 999));
+}
+
+// Test thread-safety of getDaysInMonth (implicit test)
+TEST_F(Quantity_DateTest, MultipleLeapYearChecks)
+{
+ // Repeatedly check different months and years
+ // This would catch thread-safety issues with mutable month_table
+ for (int year = 2000; year <= 2024; ++year)
+ {
+ for (int month = 1; month <= 12; ++month)
+ {
+ int maxDay;
+ if (month == 2)
+ {
+ maxDay = Quantity_Date::IsLeap(year) ? 29 : 28;
+ }
+ else if (month == 4 || month == 6 || month == 9 || month == 11)
+ {
+ maxDay = 30;
+ }
+ else
+ {
+ maxDay = 31;
+ }
+
+ EXPECT_TRUE(Quantity_Date::IsValid(month, maxDay, year, 0, 0, 0, 0, 0));
+ EXPECT_FALSE(Quantity_Date::IsValid(month, maxDay + 1, year, 0, 0, 0, 0, 0));
+ }
+ }
+}
+
+// Test date difference with epoch (Jan 1, 1979 00:00) - special case
+TEST_F(Quantity_DateTest, DifferenceFromEpoch)
+{
+ Quantity_Date aEpoch; // Defaults to Jan 1, 1979 00:00
+ Quantity_Date aDate(1, 2, 1979, 0, 0, 0, 0, 0); // Jan 2, 1979
+
+ Quantity_Period aPeriod = aEpoch.Difference(aDate);
+
+ Standard_Integer ss, mics;
+ aPeriod.Values(ss, mics);
+
+ EXPECT_EQ(86400, ss); // 1 day = 86400 seconds
+ EXPECT_EQ(0, mics);
+}
+
+// Test date difference with microsecond underflow
+TEST_F(Quantity_DateTest, DifferenceWithMicrosecondUnderflow)
+{
+ Quantity_Date aDate1(1, 1, 2020, 0, 0, 1, 200, 0); // 1.2 seconds
+ Quantity_Date aDate2(1, 1, 2020, 0, 0, 0, 500, 0); // 0.5 seconds
+
+ Quantity_Period aPeriod = aDate1.Difference(aDate2);
+
+ Standard_Integer ss, mics;
+ aPeriod.Values(ss, mics);
+
+ EXPECT_EQ(0, ss);
+ EXPECT_EQ(700000, mics); // 1.2 - 0.5 = 0.7 seconds = 700000 microseconds
+}
+
+// Test date difference with reversed dates (absolute value)
+TEST_F(Quantity_DateTest, DifferenceReversed)
+{
+ Quantity_Date aDate1(1, 1, 2020, 1, 0, 0, 0, 0); // 1 hour
+ Quantity_Date aDate2(1, 1, 2020, 3, 0, 0, 0, 0); // 3 hours
+
+ // Difference should be absolute value
+ Quantity_Period aPeriod = aDate1.Difference(aDate2);
+
+ Standard_Integer ss, mics;
+ aPeriod.Values(ss, mics);
+
+ EXPECT_EQ(7200, ss); // 2 hours = 7200 seconds
+ EXPECT_EQ(0, mics);
+}
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#include <Quantity_Period.hxx>
+
+#include <gtest/gtest.h>
+
+// Test fixture for Quantity_Period tests
+class Quantity_PeriodTest : public testing::Test
+{
+protected:
+ void SetUp() override {}
+
+ void TearDown() override {}
+};
+
+// Test basic construction
+TEST_F(Quantity_PeriodTest, BasicConstruction)
+{
+ // Constructor with days, hours, minutes, seconds, milliseconds, microseconds
+ Quantity_Period aPeriod1(1, 2, 3, 4, 5, 6);
+
+ Standard_Integer dd, hh, mn, ss, mis, mics;
+ aPeriod1.Values(dd, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(1, dd);
+ EXPECT_EQ(2, hh);
+ EXPECT_EQ(3, mn);
+ EXPECT_EQ(4, ss);
+ EXPECT_EQ(5, mis);
+ EXPECT_EQ(6, mics);
+
+ // Constructor with seconds and microseconds
+ Quantity_Period aPeriod2(3600, 500000); // 1 hour, 500000 microseconds
+
+ Standard_Integer ss2, mics2;
+ aPeriod2.Values(ss2, mics2);
+
+ EXPECT_EQ(3600, ss2); // SECONDS_PER_HOUR
+ EXPECT_EQ(500000, mics2);
+}
+
+// Test constexpr comparison operators
+TEST_F(Quantity_PeriodTest, ConstexprComparisons)
+{
+ Quantity_Period aPeriod1(0, 0, 0, 100, 0, 0);
+ Quantity_Period aPeriod2(0, 0, 0, 100, 0, 0);
+ Quantity_Period aPeriod3(0, 0, 0, 200, 0, 0);
+
+ // Test IsEqual (constexpr)
+ EXPECT_TRUE(aPeriod1.IsEqual(aPeriod2));
+ EXPECT_FALSE(aPeriod1.IsEqual(aPeriod3));
+
+ // Test IsShorter (constexpr)
+ EXPECT_TRUE(aPeriod1.IsShorter(aPeriod3));
+ EXPECT_FALSE(aPeriod3.IsShorter(aPeriod1));
+ EXPECT_FALSE(aPeriod1.IsShorter(aPeriod2));
+
+ // Test IsLonger (constexpr)
+ EXPECT_TRUE(aPeriod3.IsLonger(aPeriod1));
+ EXPECT_FALSE(aPeriod1.IsLonger(aPeriod3));
+ EXPECT_FALSE(aPeriod1.IsLonger(aPeriod2));
+}
+
+// Test validation
+TEST_F(Quantity_PeriodTest, Validation)
+{
+ // Valid periods
+ EXPECT_TRUE(Quantity_Period::IsValid(1, 2, 3, 4, 5, 6));
+ EXPECT_TRUE(Quantity_Period::IsValid(0, 0, 0, 0, 0, 0));
+
+ // Invalid periods (negative values)
+ EXPECT_FALSE(Quantity_Period::IsValid(-1, 0, 0, 0, 0, 0));
+ EXPECT_FALSE(Quantity_Period::IsValid(0, -1, 0, 0, 0, 0));
+ EXPECT_FALSE(Quantity_Period::IsValid(0, 0, -1, 0, 0, 0));
+ EXPECT_FALSE(Quantity_Period::IsValid(0, 0, 0, -1, 0, 0));
+ EXPECT_FALSE(Quantity_Period::IsValid(0, 0, 0, 0, -1, 0));
+ EXPECT_FALSE(Quantity_Period::IsValid(0, 0, 0, 0, 0, -1));
+
+ // Validation with seconds and microseconds only
+ EXPECT_TRUE(Quantity_Period::IsValid(100, 500));
+ EXPECT_FALSE(Quantity_Period::IsValid(-1, 500));
+ EXPECT_FALSE(Quantity_Period::IsValid(100, -1));
+}
+
+// Test SetValues and Values round-trip (tests time constants)
+TEST_F(Quantity_PeriodTest, SetValuesRoundTrip)
+{
+ Quantity_Period aPeriod(0, 0); // Initialize with zero period
+ aPeriod.SetValues(2, 3, 4, 5, 6, 7);
+
+ Standard_Integer dd, hh, mn, ss, mis, mics;
+ aPeriod.Values(dd, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(2, dd);
+ EXPECT_EQ(3, hh);
+ EXPECT_EQ(4, mn);
+ EXPECT_EQ(5, ss);
+ EXPECT_EQ(6, mis);
+ EXPECT_EQ(7, mics);
+}
+
+// Test conversion between formats (uses SECONDS_PER_DAY, etc.)
+TEST_F(Quantity_PeriodTest, FormatConversion)
+{
+ // 1 day should equal 86400 seconds (SECONDS_PER_DAY)
+ Quantity_Period aPeriod1(1, 0, 0, 0, 0, 0);
+
+ Standard_Integer ss, mics;
+ aPeriod1.Values(ss, mics);
+
+ EXPECT_EQ(86400, ss); // SECONDS_PER_DAY = 24 * 3600
+
+ // 1 hour should equal 3600 seconds (SECONDS_PER_HOUR)
+ Quantity_Period aPeriod2(0, 1, 0, 0, 0, 0);
+ aPeriod2.Values(ss, mics);
+
+ EXPECT_EQ(3600, ss); // SECONDS_PER_HOUR
+
+ // 1 minute should equal 60 seconds (SECONDS_PER_MINUTE)
+ Quantity_Period aPeriod3(0, 0, 1, 0, 0, 0);
+ aPeriod3.Values(ss, mics);
+
+ EXPECT_EQ(60, ss); // SECONDS_PER_MINUTE
+}
+
+// Test millisecond to microsecond conversion (uses USECS_PER_MSEC)
+TEST_F(Quantity_PeriodTest, MillisecondConversion)
+{
+ // 1 millisecond = 1000 microseconds (USECS_PER_MSEC)
+ Quantity_Period aPeriod(0, 0, 0, 0, 1, 0);
+
+ Standard_Integer ss, mics;
+ aPeriod.Values(ss, mics);
+
+ EXPECT_EQ(0, ss);
+ EXPECT_EQ(1000, mics); // USECS_PER_MSEC
+
+ // Mixed milliseconds and microseconds
+ Quantity_Period aPeriod2(0, 0, 0, 0, 5, 250);
+ aPeriod2.Values(ss, mics);
+
+ EXPECT_EQ(0, ss);
+ EXPECT_EQ(5250, mics); // 5 * 1000 + 250
+}
+
+// Test microsecond overflow (uses USECS_PER_SEC constant)
+TEST_F(Quantity_PeriodTest, MicrosecondOverflow)
+{
+ // Create period with > 1,000,000 microseconds
+ Quantity_Period aPeriod(0, 0); // Initialize with zero period
+ aPeriod.SetValues(0, 1500000); // 1.5 seconds in microseconds
+
+ Standard_Integer ss, mics;
+ aPeriod.Values(ss, mics);
+
+ EXPECT_EQ(1, ss); // 1 second
+ EXPECT_EQ(500000, mics); // 0.5 second = 500000 microseconds
+}
+
+// Test adding periods (uses USECS_PER_SEC constant)
+TEST_F(Quantity_PeriodTest, AddPeriods)
+{
+ Quantity_Period aPeriod1(0, 1, 0, 0, 0, 0); // 1 hour
+ Quantity_Period aPeriod2(0, 2, 0, 0, 0, 0); // 2 hours
+
+ Quantity_Period aResult = aPeriod1.Add(aPeriod2);
+
+ Standard_Integer ss, mics;
+ aResult.Values(ss, mics);
+
+ EXPECT_EQ(10800, ss); // 3 hours = 3 * 3600 = 10800 seconds
+ EXPECT_EQ(0, mics);
+}
+
+// Test adding with microsecond overflow
+TEST_F(Quantity_PeriodTest, AddWithMicrosecondOverflow)
+{
+ Quantity_Period aPeriod1(0, 0, 0, 0, 0, 600000); // 0.6 seconds
+ Quantity_Period aPeriod2(0, 0, 0, 0, 0, 600000); // 0.6 seconds
+
+ Quantity_Period aResult = aPeriod1.Add(aPeriod2);
+
+ Standard_Integer ss, mics;
+ aResult.Values(ss, mics);
+
+ EXPECT_EQ(1, ss); // Should overflow to 1 second
+ EXPECT_EQ(200000, mics); // Remaining 0.2 seconds
+}
+
+// Test subtracting periods (uses USECS_PER_SEC constant)
+TEST_F(Quantity_PeriodTest, SubtractPeriods)
+{
+ Quantity_Period aPeriod1(0, 3, 0, 0, 0, 0); // 3 hours
+ Quantity_Period aPeriod2(0, 1, 0, 0, 0, 0); // 1 hour
+
+ Quantity_Period aResult = aPeriod1.Subtract(aPeriod2);
+
+ Standard_Integer ss, mics;
+ aResult.Values(ss, mics);
+
+ EXPECT_EQ(7200, ss); // 2 hours = 2 * 3600
+ EXPECT_EQ(0, mics);
+}
+
+// Test subtracting with microsecond underflow
+TEST_F(Quantity_PeriodTest, SubtractWithMicrosecondUnderflow)
+{
+ Quantity_Period aPeriod1(0, 0, 0, 1, 0, 200000); // 1.2 seconds
+ Quantity_Period aPeriod2(0, 0, 0, 0, 0, 500000); // 0.5 seconds
+
+ Quantity_Period aResult = aPeriod1.Subtract(aPeriod2);
+
+ Standard_Integer ss, mics;
+ aResult.Values(ss, mics);
+
+ EXPECT_EQ(0, ss);
+ EXPECT_EQ(700000, mics); // 1.2 - 0.5 = 0.7 seconds
+}
+
+// Test subtracting larger from smaller (absolute value)
+TEST_F(Quantity_PeriodTest, SubtractNegative)
+{
+ Quantity_Period aPeriod1(0, 1, 0, 0, 0, 0); // 1 hour
+ Quantity_Period aPeriod2(0, 3, 0, 0, 0, 0); // 3 hours
+
+ Quantity_Period aResult = aPeriod1.Subtract(aPeriod2);
+
+ Standard_Integer ss, mics;
+ aResult.Values(ss, mics);
+
+ // Result should be absolute value (2 hours)
+ EXPECT_EQ(7200, ss); // 2 * 3600
+ EXPECT_EQ(0, mics);
+}
+
+// Test complex period calculations
+TEST_F(Quantity_PeriodTest, ComplexCalculations)
+{
+ // 1 day, 2 hours, 30 minutes, 45 seconds, 500 milliseconds, 250 microseconds
+ Quantity_Period aPeriod(1, 2, 30, 45, 500, 250);
+
+ Standard_Integer ss, mics;
+ aPeriod.Values(ss, mics);
+
+ // Calculate expected seconds:
+ // 1 day = 86400 seconds
+ // 2 hours = 7200 seconds
+ // 30 minutes = 1800 seconds
+ // 45 seconds = 45 seconds
+ // Total = 95445 seconds
+ EXPECT_EQ(95445, ss);
+
+ // Calculate expected microseconds:
+ // 500 milliseconds = 500000 microseconds
+ // 250 microseconds = 250 microseconds
+ // Total = 500250 microseconds
+ EXPECT_EQ(500250, mics);
+}
+
+// Test extraction back to components (uses all time constants)
+TEST_F(Quantity_PeriodTest, ComponentExtraction)
+{
+ Quantity_Period aPeriod(2, 3, 45, 30, 123, 456);
+
+ Standard_Integer dd, hh, mn, ss, mis, mics;
+ aPeriod.Values(dd, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(2, dd);
+ EXPECT_EQ(3, hh);
+ EXPECT_EQ(45, mn);
+ EXPECT_EQ(30, ss);
+ EXPECT_EQ(123, mis);
+ EXPECT_EQ(456, mics);
+}
+
+// Test zero period
+TEST_F(Quantity_PeriodTest, ZeroPeriod)
+{
+ Quantity_Period aPeriod(0, 0, 0, 0, 0, 0);
+
+ Standard_Integer dd, hh, mn, ss, mis, mics;
+ aPeriod.Values(dd, hh, mn, ss, mis, mics);
+
+ EXPECT_EQ(0, dd);
+ EXPECT_EQ(0, hh);
+ EXPECT_EQ(0, mn);
+ EXPECT_EQ(0, ss);
+ EXPECT_EQ(0, mis);
+ EXPECT_EQ(0, mics);
+
+ // Zero period should be equal to itself
+ Quantity_Period aZero2(0, 0, 0, 0, 0, 0);
+ EXPECT_TRUE(aPeriod.IsEqual(aZero2));
+}
+
+// Test large values (stress test for constants)
+TEST_F(Quantity_PeriodTest, LargeValues)
+{
+ // 100 days
+ Quantity_Period aPeriod(100, 0, 0, 0, 0, 0);
+
+ Standard_Integer ss, mics;
+ aPeriod.Values(ss, mics);
+
+ EXPECT_EQ(8640000, ss); // 100 * 86400
+ EXPECT_EQ(0, mics);
+}
+
+// Test specific time constant values (regression test)
+TEST_F(Quantity_PeriodTest, TimeConstantValues)
+{
+ // Test that 24 hours = 1 day
+ Quantity_Period aPeriod24h(0, 24, 0, 0, 0, 0);
+ Quantity_Period aPeriod1d(1, 0, 0, 0, 0, 0);
+
+ Standard_Integer ss1, mics1, ss2, mics2;
+ aPeriod24h.Values(ss1, mics1);
+ aPeriod1d.Values(ss2, mics2);
+
+ EXPECT_EQ(ss1, ss2); // Should both be 86400 seconds
+
+ // Test that 60 seconds = 1 minute
+ Quantity_Period aPeriod60s(0, 0, 0, 60, 0, 0);
+ Quantity_Period aPeriod1m(0, 0, 1, 0, 0, 0);
+
+ aPeriod60s.Values(ss1, mics1);
+ aPeriod1m.Values(ss2, mics2);
+
+ EXPECT_EQ(ss1, ss2); // Should both be 60 seconds
+
+ // Test that 60 minutes = 1 hour
+ Quantity_Period aPeriod60m(0, 0, 60, 0, 0, 0);
+ Quantity_Period aPeriod1h(0, 1, 0, 0, 0, 0);
+
+ aPeriod60m.Values(ss1, mics1);
+ aPeriod1h.Values(ss2, mics2);
+
+ EXPECT_EQ(ss1, ss2); // Should both be 3600 seconds
+}
+
+// Test that 1000 milliseconds = 1000000 microseconds = 1 second
+TEST_F(Quantity_PeriodTest, MillisecondToSecondConversion)
+{
+ Quantity_Period aPeriod(0, 0, 0, 0, 1000, 0);
+
+ Standard_Integer ss, mics;
+ aPeriod.Values(ss, mics);
+
+ EXPECT_EQ(1, ss); // Should overflow to 1 second
+ EXPECT_EQ(0, mics); // No remaining microseconds
+}
+
+// Test period subtraction with large microsecond underflow (verifies O(1) optimization)
+TEST_F(Quantity_PeriodTest, SubtractLargeMicrosecondUnderflow)
+{
+ Quantity_Period aPeriod1(0, 0, 0, 10, 0, 200000); // 10.2 seconds
+ Quantity_Period aPeriod2(0, 0, 0, 0, 0, 2700000); // 2.7 seconds
+
+ Quantity_Period aResult = aPeriod1.Subtract(aPeriod2);
+
+ Standard_Integer ss, mics;
+ aResult.Values(ss, mics);
+
+ EXPECT_EQ(7, ss); // 10.2 - 2.7 = 7.5 seconds
+ EXPECT_EQ(500000, mics);
+}
+
+// Test period subtraction with negative seconds and positive microseconds
+TEST_F(Quantity_PeriodTest, SubtractNegativeWithMicroseconds)
+{
+ Quantity_Period aPeriod1(0, 0, 0, 5, 0, 300000); // 5.3 seconds
+ Quantity_Period aPeriod2(0, 0, 0, 8, 0, 100000); // 8.1 seconds
+
+ Quantity_Period aResult = aPeriod1.Subtract(aPeriod2);
+
+ Standard_Integer ss, mics;
+ aResult.Values(ss, mics);
+
+ // Absolute value: 8.1 - 5.3 = 2.8 seconds
+ EXPECT_EQ(2, ss);
+ EXPECT_EQ(800000, mics);
+}
#include <Standard_Dump.hxx>
#include <TCollection_AsciiString.hxx>
-#define RGBHLS_H_UNDEFINED -1.0
+namespace
+{
+static constexpr float RGBHLS_H_UNDEFINED = -1.0f;
+static constexpr double DEG_TO_RAD = M_PI / 180.0;
+static constexpr double RAD_TO_DEG = 180.0 / M_PI;
+static constexpr double POW_25_7 = 6103515625.0; // 25^7 used in CIEDE2000
+static constexpr double CIELAB_EPSILON = 0.008856451679035631; // (6/29)^3
+static constexpr double CIELAB_KAPPA = 7.787037037037037; // (1/3) * (29/6)^2
+static constexpr double CIELAB_OFFSET = 16.0 / 116.0;
+static constexpr double CIELAB_L_COEFF = 116.0;
+static constexpr double CIELAB_A_COEFF = 500.0;
+static constexpr double CIELAB_B_COEFF = 200.0;
+static constexpr double CIELAB_L_OFFSET = 16.0;
+// D65 / 2 deg (CIE 1931) standard illuminant reference white point
+static constexpr double D65_REF_X = 95.047;
+static constexpr double D65_REF_Y = 100.000;
+static constexpr double D65_REF_Z = 108.883;
+// sRGB to XYZ conversion matrix (D65 illuminant, 2 deg observer)
+static constexpr double RGB_TO_XYZ_R_X = 0.4124564;
+static constexpr double RGB_TO_XYZ_R_Y = 0.2126729;
+static constexpr double RGB_TO_XYZ_R_Z = 0.0193339;
+static constexpr double RGB_TO_XYZ_G_X = 0.3575761;
+static constexpr double RGB_TO_XYZ_G_Y = 0.7151522;
+static constexpr double RGB_TO_XYZ_G_Z = 0.1191920;
+static constexpr double RGB_TO_XYZ_B_X = 0.1804375;
+static constexpr double RGB_TO_XYZ_B_Y = 0.0721750;
+static constexpr double RGB_TO_XYZ_B_Z = 0.9503041;
+// XYZ to sRGB conversion matrix (D65 illuminant, 2 deg observer)
+static constexpr double XYZ_TO_RGB_X_R = 3.2404542;
+static constexpr double XYZ_TO_RGB_X_G = -0.9692660;
+static constexpr double XYZ_TO_RGB_X_B = 0.0556434;
+static constexpr double XYZ_TO_RGB_Y_R = -1.5371385;
+static constexpr double XYZ_TO_RGB_Y_G = 1.8760108;
+static constexpr double XYZ_TO_RGB_Y_B = -0.2040259;
+static constexpr double XYZ_TO_RGB_Z_R = -0.4985314;
+static constexpr double XYZ_TO_RGB_Z_G = 0.0415560;
+static constexpr double XYZ_TO_RGB_Z_B = 1.0572252;
+} // namespace
-static Standard_Real TheEpsilon = 0.0001;
+namespace
+{
+// Returns a reference to the epsilon value.
+inline Standard_Real& getEpsilonRef() noexcept
+{
+ static Standard_Real theEpsilon = 0.0001;
+ return theEpsilon;
+}
-// Throw exception if RGB values are out of range.
-#define Quantity_ColorValidateRgbRange(theR, theG, theB) \
- if (theR < 0.0 || theR > 1.0 || theG < 0.0 || theG > 1.0 || theB < 0.0 || theB > 1.0) \
- { \
- throw Standard_OutOfRange("Color out"); \
+// Validate RGB values are in range [0, 1].
+inline void validateRgbRange(Standard_Real theR, Standard_Real theG, Standard_Real theB)
+{
+ if (theR < 0.0 || theR > 1.0 || theG < 0.0 || theG > 1.0 || theB < 0.0 || theB > 1.0)
+ {
+ throw Standard_OutOfRange("Color out");
}
+}
-// Throw exception if HLS values are out of range.
-#define Quantity_ColorValidateHlsRange(theH, theL, theS) \
- if ((theH < 0.0 && theH != RGBHLS_H_UNDEFINED && theS != 0.0) || (theH > 360.0) || theL < 0.0 \
- || theL > 1.0 || theS < 0.0 || theS > 1.0) \
- { \
- throw Standard_OutOfRange("Color out"); \
+// Validate HLS values are in valid ranges.
+inline void validateHlsRange(Standard_Real theH, Standard_Real theL, Standard_Real theS)
+{
+ if ((theH < 0.0 && theH != RGBHLS_H_UNDEFINED && theS != 0.0) || (theH > 360.0) || theL < 0.0
+ || theL > 1.0 || theS < 0.0 || theS > 1.0)
+ {
+ throw Standard_OutOfRange("Color out");
}
+}
-// Throw exception if CIELab color values are out of range.
-#define Quantity_ColorValidateLabRange(theL, thea, theb) \
- if (theL < 0. || theL > 100. || thea < -100. || thea > 100. || theb < -110. || theb > 100.) \
- { \
- throw Standard_OutOfRange("Color out"); \
+// Validate CIELab color values are in valid ranges.
+inline void validateLabRange(Standard_Real theL, Standard_Real thea, Standard_Real theb)
+{
+ if (theL < 0. || theL > 100. || thea < -100. || thea > 100. || theb < -110. || theb > 100.)
+ {
+ throw Standard_OutOfRange("Color out");
}
+}
-// Throw exception if CIELch color values are out of range.
-#define Quantity_ColorValidateLchRange(theL, thec, theh) \
- if (theL < 0. || theL > 100. || thec < 0. || thec > 135. || theh < 0.0 || theh > 360.) \
- { \
- throw Standard_OutOfRange("Color out"); \
+// Validate CIELch color values are in valid ranges.
+inline void validateLchRange(Standard_Real theL, Standard_Real thec, Standard_Real theh)
+{
+ if (theL < 0. || theL > 100. || thec < 0. || thec > 135. || theh < 0.0 || theh > 360.)
+ {
+ throw Standard_OutOfRange("Color out");
}
+}
+} // anonymous namespace
namespace
{
NCollection_Vec3<float> RgbValues;
Quantity_NameOfColor EnumName;
- Quantity_StandardColor(Quantity_NameOfColor theName,
- const char* theStringName,
- const NCollection_Vec3<float>& thesRGB,
- const NCollection_Vec3<float>& theRGB)
+ constexpr Quantity_StandardColor(Quantity_NameOfColor theName,
+ const char* theStringName,
+ const NCollection_Vec3<float>& thesRGB,
+ const NCollection_Vec3<float>& theRGB) noexcept
: StringName(theStringName),
sRgbValues(thesRGB),
RgbValues(theRGB),
NCollection_Vec3<float>(theR##f, theG##f, theB##f))
//! Name list of standard materials (defined within enumeration).
-static const Quantity_StandardColor THE_COLORS[] = {
+static constexpr Quantity_StandardColor THE_COLORS[] = {
#include "Quantity_ColorTable.pxx"
};
//=================================================================================================
-Standard_Real Quantity_Color::Epsilon()
+Standard_Real Quantity_Color::Epsilon() noexcept
{
- return TheEpsilon;
+ return getEpsilonRef();
}
//=================================================================================================
-void Quantity_Color::SetEpsilon(const Standard_Real theEpsilon)
+void Quantity_Color::SetEpsilon(const Standard_Real theEpsilon) noexcept
{
- TheEpsilon = theEpsilon;
+ getEpsilonRef() = theEpsilon;
}
//=================================================================================================
//=================================================================================================
-Standard_CString Quantity_Color::StringName(const Quantity_NameOfColor theName)
+Standard_CString Quantity_Color::StringName(const Quantity_NameOfColor theName) noexcept
{
if ((Standard_Integer)theName < 0 || (Standard_Integer)theName > Quantity_NOC_WHITE)
{
- throw Standard_OutOfRange("Bad name");
+ return "UNDEFINED";
}
return THE_COLORS[theName].StringName;
}
//=================================================================================================
Standard_Boolean Quantity_Color::ColorFromName(const Standard_CString theName,
- Quantity_NameOfColor& theColor)
+ Quantity_NameOfColor& theColor) noexcept
{
TCollection_AsciiString aName(theName);
aName.UpperCase();
Quantity_Color::Quantity_Color(const NCollection_Vec3<float>& theRgb)
: myRgb(theRgb)
{
- Quantity_ColorValidateRgbRange(theRgb.r(), theRgb.g(), theRgb.b());
+ validateRgbRange(theRgb.r(), theRgb.g(), theRgb.b());
}
//=================================================================================================
switch (theType)
{
case Quantity_TOC_RGB: {
- Quantity_ColorValidateRgbRange(theC1, theC2, theC3);
+ validateRgbRange(theC1, theC2, theC3);
myRgb.SetValues(float(theC1), float(theC2), float(theC3));
break;
}
case Quantity_TOC_sRGB: {
- Quantity_ColorValidateRgbRange(theC1, theC2, theC3);
+ validateRgbRange(theC1, theC2, theC3);
myRgb.SetValues((float)Convert_sRGB_To_LinearRGB(theC1),
(float)Convert_sRGB_To_LinearRGB(theC2),
(float)Convert_sRGB_To_LinearRGB(theC3));
break;
}
case Quantity_TOC_HLS: {
- Quantity_ColorValidateHlsRange(theC1, theC2, theC3);
+ validateHlsRange(theC1, theC2, theC3);
myRgb =
Convert_HLS_To_LinearRGB(NCollection_Vec3<float>(float(theC1), float(theC2), float(theC3)));
break;
}
case Quantity_TOC_CIELab: {
- Quantity_ColorValidateLabRange(theC1, theC2, theC3);
+ validateLabRange(theC1, theC2, theC3);
myRgb =
Convert_Lab_To_LinearRGB(NCollection_Vec3<float>(float(theC1), float(theC2), float(theC3)));
break;
}
case Quantity_TOC_CIELch: {
- Quantity_ColorValidateLchRange(theC1, theC2, theC3);
+ validateLchRange(theC1, theC2, theC3);
myRgb = Convert_Lab_To_LinearRGB(
Convert_Lch_To_Lab(NCollection_Vec3<float>(float(theC1), float(theC2), float(theC3))));
break;
Standard_Real aLx_mean = 0.5 * (aL1 + aL2);
// mean C
- Standard_Real aC1 = Sqrt(aa1 * aa1 + ab1 * ab1);
- Standard_Real aC2 = Sqrt(aa2 * aa2 + ab2 * ab2);
- Standard_Real aC_mean = 0.5 * (aC1 + aC2);
- Standard_Real aC_mean_pow7 = Pow(aC_mean, 7);
- static const double a25_pow7 = Pow(25., 7);
- Standard_Real aG = 0.5 * (1. - Sqrt(aC_mean_pow7 / (aC_mean_pow7 + a25_pow7)));
- Standard_Real aa1x = aa1 * (1. + aG);
- Standard_Real aa2x = aa2 * (1. + aG);
- Standard_Real aC1x = Sqrt(aa1x * aa1x + ab1 * ab1);
- Standard_Real aC2x = Sqrt(aa2x * aa2x + ab2 * ab2);
- Standard_Real aCx_mean = 0.5 * (aC1x + aC2x);
+ Standard_Real aC1 = Sqrt(aa1 * aa1 + ab1 * ab1);
+ Standard_Real aC2 = Sqrt(aa2 * aa2 + ab2 * ab2);
+ Standard_Real aC_mean = 0.5 * (aC1 + aC2);
+ Standard_Real aC_mean_pow7 = Pow(aC_mean, 7);
+ Standard_Real aG = 0.5 * (1. - Sqrt(aC_mean_pow7 / (aC_mean_pow7 + POW_25_7)));
+ Standard_Real aa1x = aa1 * (1. + aG);
+ Standard_Real aa2x = aa2 * (1. + aG);
+ Standard_Real aC1x = Sqrt(aa1x * aa1x + ab1 * ab1);
+ Standard_Real aC2x = Sqrt(aa2x * aa2x + ab2 * ab2);
+ Standard_Real aCx_mean = 0.5 * (aC1x + aC2x);
// mean H
- Standard_Real ah1x = (aC1x > TheEpsilon ? ATan2(ab1, aa1x) * 180. / M_PI : 270.);
- Standard_Real ah2x = (aC2x > TheEpsilon ? ATan2(ab2, aa2x) * 180. / M_PI : 270.);
+ Standard_Real ah1x = (aC1x > Epsilon() ? ATan2(ab1, aa1x) * RAD_TO_DEG : 270.);
+ Standard_Real ah2x = (aC2x > Epsilon() ? ATan2(ab2, aa2x) * RAD_TO_DEG : 270.);
if (ah1x < 0.)
ah1x += 360.;
if (ah2x < 0.)
// deltas
Standard_Real aDeltaLx = aL2 - aL1;
Standard_Real aDeltaCx = aC2x - aC1x;
- Standard_Real aDeltaHx = 2. * Sqrt(aC1x * aC2x) * Sin(0.5 * aDeltahx * M_PI / 180.);
+ Standard_Real aDeltaHx = 2. * Sqrt(aC1x * aC2x) * Sin(0.5 * aDeltahx * DEG_TO_RAD);
// factors
- Standard_Real aT = 1. - 0.17 * Cos((aHx_mean - 30.) * M_PI / 180.)
- + 0.24 * Cos((2. * aHx_mean) * M_PI / 180.)
- + 0.32 * Cos((3. * aHx_mean + 6.) * M_PI / 180.)
- - 0.20 * Cos((4. * aHx_mean - 63.) * M_PI / 180.);
+ Standard_Real aT = 1. - 0.17 * Cos((aHx_mean - 30.) * DEG_TO_RAD)
+ + 0.24 * Cos((2. * aHx_mean) * DEG_TO_RAD)
+ + 0.32 * Cos((3. * aHx_mean + 6.) * DEG_TO_RAD)
+ - 0.20 * Cos((4. * aHx_mean - 63.) * DEG_TO_RAD);
Standard_Real aLx_mean50_2 = (aLx_mean - 50.) * (aLx_mean - 50.);
Standard_Real aS_L = 1. + 0.015 * aLx_mean50_2 / Sqrt(20. + aLx_mean50_2);
Standard_Real aDelta_theta = 30. * Exp(-(aHx_mean - 275.) * (aHx_mean - 275.) / 625.);
Standard_Real aCx_mean_pow7 = Pow(aCx_mean, 7);
- Standard_Real aR_C = 2. * Sqrt(aCx_mean_pow7 / (aCx_mean_pow7 + a25_pow7));
- Standard_Real aR_T = -aR_C * Sin(2. * aDelta_theta * M_PI / 180.);
+ Standard_Real aR_C = 2. * Sqrt(aCx_mean_pow7 / (aCx_mean_pow7 + POW_25_7));
+ Standard_Real aR_T = -aR_C * Sin(2. * aDelta_theta * DEG_TO_RAD);
// finally, the difference
Standard_Real aDL = aDeltaLx / aS_L;
// function : Convert_sRGB_To_HLS
// purpose : Reference: La synthese d'images, Collection Hermes
// =======================================================================
-NCollection_Vec3<float> Quantity_Color::Convert_sRGB_To_HLS(const NCollection_Vec3<float>& theRgb)
+NCollection_Vec3<float> Quantity_Color::Convert_sRGB_To_HLS(
+ const NCollection_Vec3<float>& theRgb) noexcept
{
float aPlus = 0.0f;
float aDiff = theRgb.g() - theRgb.b();
// purpose : non-linear function transforming XYZ coordinates to CIE Lab
// see http://www.brucelindbloom.com/index.html?Equations.html
// =======================================================================
-static inline double CIELab_f(double theValue)
+static inline double CIELab_f(double theValue) noexcept
{
- return theValue > 0.008856451679035631 ? Pow(theValue, 1. / 3.)
- : (7.787037037037037 * theValue) + 16. / 116.;
+ return theValue > CIELAB_EPSILON ? Pow(theValue, 1. / 3.)
+ : (CIELAB_KAPPA * theValue) + CIELAB_OFFSET;
}
// =======================================================================
// purpose : inverse of non-linear function transforming XYZ coordinates to CIE Lab
// see http://www.brucelindbloom.com/index.html?Equations.html
// =======================================================================
-static inline double CIELab_invertf(double theValue)
+static inline double CIELab_invertf(double theValue) noexcept
{
double aV3 = theValue * theValue * theValue;
- return aV3 > 0.008856451679035631 ? aV3 : (theValue - 16. / 116.) / 7.787037037037037;
+ return aV3 > CIELAB_EPSILON ? aV3 : (theValue - CIELAB_OFFSET) / CIELAB_KAPPA;
}
// =======================================================================
// see https://www.easyrgb.com/en/math.php
// =======================================================================
NCollection_Vec3<float> Quantity_Color::Convert_LinearRGB_To_Lab(
- const NCollection_Vec3<float>& theRgb)
+ const NCollection_Vec3<float>& theRgb) noexcept
{
double aR = theRgb[0];
double aG = theRgb[1];
// convert to XYZ normalized to D65 / 2 deg (CIE 1931) standard illuminant intensities
// see http://www.brucelindbloom.com/index.html?Equations.html
- double aX = (aR * 0.4124564 + aG * 0.3575761 + aB * 0.1804375) * 100. / 95.047;
- double aY = (aR * 0.2126729 + aG * 0.7151522 + aB * 0.0721750) * 100. / 100.000;
- double aZ = (aR * 0.0193339 + aG * 0.1191920 + aB * 0.9503041) * 100. / 108.883;
+ double aX = (aR * RGB_TO_XYZ_R_X + aG * RGB_TO_XYZ_G_X + aB * RGB_TO_XYZ_B_X) * 100. / D65_REF_X;
+ double aY = (aR * RGB_TO_XYZ_R_Y + aG * RGB_TO_XYZ_G_Y + aB * RGB_TO_XYZ_B_Y);
+ double aZ = (aR * RGB_TO_XYZ_R_Z + aG * RGB_TO_XYZ_G_Z + aB * RGB_TO_XYZ_B_Z) * 100. / D65_REF_Z;
// convert to Lab
double afX = CIELab_f(aX);
double afY = CIELab_f(aY);
double afZ = CIELab_f(aZ);
- double aL = 116. * afY - 16.;
- double aa = 500. * (afX - afY);
- double ab = 200. * (afY - afZ);
+ double aL = CIELAB_L_COEFF * afY - CIELAB_L_OFFSET;
+ double aa = CIELAB_A_COEFF * (afX - afY);
+ double ab = CIELAB_B_COEFF * (afY - afZ);
return NCollection_Vec3<float>((float)aL, (float)aa, (float)ab);
}
// see https://www.easyrgb.com/en/math.php
// =======================================================================
NCollection_Vec3<float> Quantity_Color::Convert_Lab_To_LinearRGB(
- const NCollection_Vec3<float>& theLab)
+ const NCollection_Vec3<float>& theLab) noexcept
{
double aL = theLab[0];
double aa = theLab[1];
double aC = aRate / (double)NBSTEPS;
// convert to XYZ for D65 / 2 deg (CIE 1931) standard illuminant
- double afY = (aL + 16.) / 116.;
- double afX = aC * aa / 500. + afY;
- double afZ = afY - aC * ab / 200.;
+ double afY = (aL + CIELAB_L_OFFSET) / CIELAB_L_COEFF;
+ double afX = aC * aa / CIELAB_A_COEFF + afY;
+ double afZ = afY - aC * ab / CIELAB_B_COEFF;
- double aX = CIELab_invertf(afX) * 95.047;
- double aY = CIELab_invertf(afY) * 100.000;
- double aZ = CIELab_invertf(afZ) * 108.883;
+ double aX = CIELab_invertf(afX) * D65_REF_X;
+ double aY = CIELab_invertf(afY) * D65_REF_Y;
+ double aZ = CIELab_invertf(afZ) * D65_REF_Z;
// convert to RGB
// see http://www.brucelindbloom.com/index.html?Equations.html
- double aR = (aX * 3.2404542 + aY * -1.5371385 + aZ * -0.4985314) / 100.;
- double aG = (aX * -0.9692660 + aY * 1.8760108 + aZ * 0.0415560) / 100.;
- double aB = (aX * 0.0556434 + aY * -0.2040259 + aZ * 1.0572252) / 100.;
+ double aR = (aX * XYZ_TO_RGB_X_R + aY * XYZ_TO_RGB_Y_R + aZ * XYZ_TO_RGB_Z_R) / 100.;
+ double aG = (aX * XYZ_TO_RGB_X_G + aY * XYZ_TO_RGB_Y_G + aZ * XYZ_TO_RGB_Z_G) / 100.;
+ double aB = (aX * XYZ_TO_RGB_X_B + aY * XYZ_TO_RGB_Y_B + aZ * XYZ_TO_RGB_Z_B) / 100.;
// exit if we are in range or at zero C
if (aRate == 0 || (aR >= 0. && aR <= 1. && aG >= 0. && aG <= 1. && aB >= 0. && aB <= 1.))
// purpose : convert CIE Lab color to CIE Lch color
// see https://www.easyrgb.com/en/math.php
// =======================================================================
-NCollection_Vec3<float> Quantity_Color::Convert_Lab_To_Lch(const NCollection_Vec3<float>& theLab)
+NCollection_Vec3<float> Quantity_Color::Convert_Lab_To_Lch(
+ const NCollection_Vec3<float>& theLab) noexcept
{
double aa = theLab[1];
double ab = theLab[2];
double aC = Sqrt(aa * aa + ab * ab);
- double aH = (aC > TheEpsilon ? ATan2(ab, aa) * 180. / M_PI : 0.);
+ double aH = (aC > Epsilon() ? ATan2(ab, aa) * RAD_TO_DEG : 0.);
if (aH < 0.)
aH += 360.;
// purpose : convert CIE Lch color to CIE Lab color
// see https://www.easyrgb.com/en/math.php
// =======================================================================
-NCollection_Vec3<float> Quantity_Color::Convert_Lch_To_Lab(const NCollection_Vec3<float>& theLch)
+NCollection_Vec3<float> Quantity_Color::Convert_Lch_To_Lab(
+ const NCollection_Vec3<float>& theLch) noexcept
{
double aC = theLch[1];
double aH = theLch[2];
- aH *= M_PI / 180.;
+ aH *= DEG_TO_RAD;
double aa = aC * Cos(aH);
double ab = aC * Sin(aH);
Standard_EXPORT Quantity_NameOfColor Name() const;
//! Updates the color from specified named color.
- void SetValues(const Quantity_NameOfColor theName)
+ void SetValues(const Quantity_NameOfColor theName) noexcept
{
myRgb = valuesOf(theName, Quantity_TOC_RGB);
}
//! Return the color as vector of 3 float elements.
- const NCollection_Vec3<float>& Rgb() const { return myRgb; }
+ constexpr const NCollection_Vec3<float>& Rgb() const noexcept { return myRgb; }
//! Return the color as vector of 3 float elements.
- operator const NCollection_Vec3<float>&() const { return myRgb; }
+ constexpr operator const NCollection_Vec3<float>&() const noexcept { return myRgb; }
//! Returns in theC1, theC2 and theC3 the components of this color
//! according to the color system definition theType.
const Quantity_TypeOfColor theType);
//! Returns the Red component (quantity of red) of the color within range [0.0; 1.0].
- Standard_Real Red() const { return myRgb.r(); }
+ constexpr Standard_Real Red() const noexcept { return myRgb.r(); }
//! Returns the Green component (quantity of green) of the color within range [0.0; 1.0].
- Standard_Real Green() const { return myRgb.g(); }
+ constexpr Standard_Real Green() const noexcept { return myRgb.g(); }
//! Returns the Blue component (quantity of blue) of the color within range [0.0; 1.0].
- Standard_Real Blue() const { return myRgb.b(); }
+ constexpr Standard_Real Blue() const noexcept { return myRgb.b(); }
//! Returns the Hue component (hue angle) of the color
//! in degrees within range [0.0; 360.0], 0.0 being Red.
//! -1.0 is a special value reserved for grayscale color (S should be 0.0)
- Standard_Real Hue() const { return Convert_LinearRGB_To_HLS(myRgb)[0]; }
+ Standard_Real Hue() const noexcept { return Convert_LinearRGB_To_HLS(myRgb)[0]; }
//! Returns the Light component (value of the lightness) of the color within range [0.0; 1.0].
- Standard_Real Light() const { return Convert_LinearRGB_To_HLS(myRgb)[1]; }
+ Standard_Real Light() const noexcept { return Convert_LinearRGB_To_HLS(myRgb)[1]; }
//! Increases or decreases the intensity (variation of the lightness).
//! The delta is a percentage. Any value greater than zero will increase the intensity.
//! Returns the Saturation component (value of the saturation) of the color within range
//! [0.0; 1.0].
- Standard_Real Saturation() const { return Convert_LinearRGB_To_HLS(myRgb)[2]; }
+ Standard_Real Saturation() const noexcept { return Convert_LinearRGB_To_HLS(myRgb)[2]; }
//! Increases or decreases the contrast (variation of the saturation).
//! The delta is a percentage. Any value greater than zero will increase the contrast.
Standard_EXPORT void ChangeContrast(const Standard_Real theDelta);
//! Returns TRUE if the distance between two colors is greater than Epsilon().
- Standard_Boolean IsDifferent(const Quantity_Color& theOther) const
+ Standard_Boolean IsDifferent(const Quantity_Color& theOther) const noexcept
{
return (SquareDistance(theOther) > Epsilon() * Epsilon());
}
//! Alias to IsDifferent().
- Standard_Boolean operator!=(const Quantity_Color& theOther) const
+ Standard_Boolean operator!=(const Quantity_Color& theOther) const noexcept
{
return IsDifferent(theOther);
}
//! Returns TRUE if the distance between two colors is no greater than Epsilon().
- Standard_Boolean IsEqual(const Quantity_Color& theOther) const
+ Standard_Boolean IsEqual(const Quantity_Color& theOther) const noexcept
{
return (SquareDistance(theOther) <= Epsilon() * Epsilon());
}
//! Alias to IsEqual().
- Standard_Boolean operator==(const Quantity_Color& theOther) const { return IsEqual(theOther); }
+ Standard_Boolean operator==(const Quantity_Color& theOther) const noexcept
+ {
+ return IsEqual(theOther);
+ }
//! Returns the distance between two colors. It's a value between 0 and the square root of 3 (the
//! black/white distance).
- Standard_Real Distance(const Quantity_Color& theColor) const
+ Standard_Real Distance(const Quantity_Color& theColor) const noexcept
{
return (NCollection_Vec3<Standard_Real>(myRgb)
- NCollection_Vec3<Standard_Real>(theColor.myRgb))
}
//! Returns the square of distance between two colors.
- Standard_Real SquareDistance(const Quantity_Color& theColor) const
+ Standard_Real SquareDistance(const Quantity_Color& theColor) const noexcept
{
return (NCollection_Vec3<Standard_Real>(myRgb)
- NCollection_Vec3<Standard_Real>(theColor.myRgb))
}
//! Returns the name of the color identified by the given Quantity_NameOfColor enumeration value.
- Standard_EXPORT static Standard_CString StringName(const Quantity_NameOfColor theColor);
+ Standard_EXPORT static Standard_CString StringName(const Quantity_NameOfColor theColor) noexcept;
//! Finds color from predefined names.
//! For example, the name of the color which corresponds to "BLACK" is Quantity_NOC_BLACK.
//! Returns FALSE if name is unknown.
Standard_EXPORT static Standard_Boolean ColorFromName(const Standard_CString theName,
- Quantity_NameOfColor& theColor);
+ Quantity_NameOfColor& theColor) noexcept;
//! Finds color from predefined names.
//! @param theColorNameString the color name
//! @param theColor a found color
//! @return false if the color name is unknown, or true if the search by color name was successful
static Standard_Boolean ColorFromName(const Standard_CString theColorNameString,
- Quantity_Color& theColor)
+ Quantity_Color& theColor) noexcept
{
Quantity_NameOfColor aColorName = Quantity_NOC_BLACK;
if (!ColorFromName(theColorNameString, aColorName))
//! Returns hex sRGB string in format "#FFAAFF".
static TCollection_AsciiString ColorToHex(const Quantity_Color& theColor,
- const bool theToPrefixHash = true)
+ const bool theToPrefixHash = true) noexcept
{
NCollection_Vec3<Standard_ShortReal> anSRgb =
Convert_LinearRGB_To_sRGB((NCollection_Vec3<Standard_ShortReal>)theColor);
//! Converts sRGB components into HLS ones.
Standard_EXPORT static NCollection_Vec3<float> Convert_sRGB_To_HLS(
- const NCollection_Vec3<float>& theRgb);
+ const NCollection_Vec3<float>& theRgb) noexcept;
//! Converts HLS components into RGB ones.
Standard_EXPORT static NCollection_Vec3<float> Convert_HLS_To_sRGB(
const NCollection_Vec3<float>& theHls);
//! Converts Linear RGB components into HLS ones.
- static NCollection_Vec3<float> Convert_LinearRGB_To_HLS(const NCollection_Vec3<float>& theRgb)
+ static NCollection_Vec3<float> Convert_LinearRGB_To_HLS(
+ const NCollection_Vec3<float>& theRgb) noexcept
{
return Convert_sRGB_To_HLS(Convert_LinearRGB_To_sRGB(theRgb));
}
//! Converts HLS components into linear RGB ones.
- static NCollection_Vec3<float> Convert_HLS_To_LinearRGB(const NCollection_Vec3<float>& theHls)
+ static NCollection_Vec3<float> Convert_HLS_To_LinearRGB(
+ const NCollection_Vec3<float>& theHls) noexcept
{
return Convert_sRGB_To_LinearRGB(Convert_HLS_To_sRGB(theHls));
}
//! Converts linear RGB components into CIE Lab ones.
Standard_EXPORT static NCollection_Vec3<float> Convert_LinearRGB_To_Lab(
- const NCollection_Vec3<float>& theRgb);
+ const NCollection_Vec3<float>& theRgb) noexcept;
//! Converts CIE Lab components into CIE Lch ones.
Standard_EXPORT static NCollection_Vec3<float> Convert_Lab_To_Lch(
- const NCollection_Vec3<float>& theLab);
+ const NCollection_Vec3<float>& theLab) noexcept;
//! Converts CIE Lab components into linear RGB ones.
//! Note that the resulting values may be out of the valid range for RGB.
Standard_EXPORT static NCollection_Vec3<float> Convert_Lab_To_LinearRGB(
- const NCollection_Vec3<float>& theLab);
+ const NCollection_Vec3<float>& theLab) noexcept;
//! Converts CIE Lch components into CIE Lab ones.
Standard_EXPORT static NCollection_Vec3<float> Convert_Lch_To_Lab(
- const NCollection_Vec3<float>& theLch);
+ const NCollection_Vec3<float>& theLch) noexcept;
//! Convert the color value to ARGB integer value, with alpha equals to 0.
//! So the output is formatted as 0x00RRGGBB.
//! as would be usually expected for RGB color packed into 4 bytes.
//! @param[in] theColor color to convert
//! @param[out] theARGB result color encoded as integer
- static void Color2argb(const Quantity_Color& theColor, Standard_Integer& theARGB)
+ static constexpr void Color2argb(const Quantity_Color& theColor,
+ Standard_Integer& theARGB) noexcept
{
const NCollection_Vec3<Standard_Integer> aColor(
static_cast<Standard_Integer>(255.0f * theColor.myRgb.r() + 0.5f),
//! Convert integer ARGB value to Color. Alpha bits are ignored.
//! Note that this packing does NOT involve linear -> non-linear sRGB conversion,
//! as would be usually expected to preserve higher (for human eye) color precision in 4 bytes.
- static void Argb2color(const Standard_Integer theARGB, Quantity_Color& theColor)
+ static void Argb2color(const Standard_Integer theARGB, Quantity_Color& theColor) noexcept
{
const NCollection_Vec3<Standard_Real> aColor(
static_cast<Standard_Real>((theARGB & 0xff0000) >> 16),
//! Convert linear RGB component into sRGB using OpenGL specs formula (double precision), also
//! known as gamma correction.
- static Standard_Real Convert_LinearRGB_To_sRGB(Standard_Real theLinearValue)
+ static Standard_Real Convert_LinearRGB_To_sRGB(Standard_Real theLinearValue) noexcept
{
return theLinearValue <= 0.0031308 ? theLinearValue * 12.92
: Pow(theLinearValue, 1.0 / 2.4) * 1.055 - 0.055;
//! Convert linear RGB component into sRGB using OpenGL specs formula (single precision), also
//! known as gamma correction.
- static float Convert_LinearRGB_To_sRGB(float theLinearValue)
+ static float Convert_LinearRGB_To_sRGB(float theLinearValue) noexcept
{
return theLinearValue <= 0.0031308f ? theLinearValue * 12.92f
: powf(theLinearValue, 1.0f / 2.4f) * 1.055f - 0.055f;
//! Convert sRGB component into linear RGB using OpenGL specs formula (double precision), also
//! known as gamma correction.
- static Standard_Real Convert_sRGB_To_LinearRGB(Standard_Real thesRGBValue)
+ static Standard_Real Convert_sRGB_To_LinearRGB(Standard_Real thesRGBValue) noexcept
{
return thesRGBValue <= 0.04045 ? thesRGBValue / 12.92
: Pow((thesRGBValue + 0.055) / 1.055, 2.4);
//! Convert sRGB component into linear RGB using OpenGL specs formula (single precision), also
//! known as gamma correction.
- static float Convert_sRGB_To_LinearRGB(float thesRGBValue)
+ static float Convert_sRGB_To_LinearRGB(float thesRGBValue) noexcept
{
return thesRGBValue <= 0.04045f ? thesRGBValue / 12.92f
: powf((thesRGBValue + 0.055f) / 1.055f, 2.4f);
//! Convert linear RGB components into sRGB using OpenGL specs formula.
template <typename T>
- static NCollection_Vec3<T> Convert_LinearRGB_To_sRGB(const NCollection_Vec3<T>& theRGB)
+ static NCollection_Vec3<T> Convert_LinearRGB_To_sRGB(const NCollection_Vec3<T>& theRGB) noexcept
{
return NCollection_Vec3<T>(Convert_LinearRGB_To_sRGB(theRGB.r()),
Convert_LinearRGB_To_sRGB(theRGB.g()),
//! Convert sRGB components into linear RGB using OpenGL specs formula.
template <typename T>
- static NCollection_Vec3<T> Convert_sRGB_To_LinearRGB(const NCollection_Vec3<T>& theRGB)
+ static NCollection_Vec3<T> Convert_sRGB_To_LinearRGB(const NCollection_Vec3<T>& theRGB) noexcept
{
return NCollection_Vec3<T>(Convert_sRGB_To_LinearRGB(theRGB.r()),
Convert_sRGB_To_LinearRGB(theRGB.g()),
}
//! Convert linear RGB component into sRGB using approximated uniform gamma coefficient 2.2.
- static float Convert_LinearRGB_To_sRGB_approx22(float theLinearValue)
+ static float Convert_LinearRGB_To_sRGB_approx22(float theLinearValue) noexcept
{
return powf(theLinearValue, 2.2f);
}
//! Convert sRGB component into linear RGB using approximated uniform gamma coefficient 2.2
- static float Convert_sRGB_To_LinearRGB_approx22(float thesRGBValue)
+ static float Convert_sRGB_To_LinearRGB_approx22(float thesRGBValue) noexcept
{
return powf(thesRGBValue, 1.0f / 2.2f);
}
//! Convert linear RGB components into sRGB using approximated uniform gamma coefficient 2.2
static NCollection_Vec3<float> Convert_LinearRGB_To_sRGB_approx22(
- const NCollection_Vec3<float>& theRGB)
+ const NCollection_Vec3<float>& theRGB) noexcept
{
return NCollection_Vec3<float>(Convert_LinearRGB_To_sRGB_approx22(theRGB.r()),
Convert_LinearRGB_To_sRGB_approx22(theRGB.g()),
//! Convert sRGB components into linear RGB using approximated uniform gamma coefficient 2.2
static NCollection_Vec3<float> Convert_sRGB_To_LinearRGB_approx22(
- const NCollection_Vec3<float>& theRGB)
+ const NCollection_Vec3<float>& theRGB) noexcept
{
return NCollection_Vec3<float>(Convert_sRGB_To_LinearRGB_approx22(theRGB.r()),
Convert_sRGB_To_LinearRGB_approx22(theRGB.g()),
const Standard_Real theS,
Standard_Real& theR,
Standard_Real& theG,
- Standard_Real& theB)
+ Standard_Real& theB) noexcept
{
const NCollection_Vec3<float> anRgb =
Convert_HLS_To_sRGB(NCollection_Vec3<float>((float)theH, (float)theL, (float)theS));
const Standard_Real theB,
Standard_Real& theH,
Standard_Real& theL,
- Standard_Real& theS)
+ Standard_Real& theS) noexcept
{
const NCollection_Vec3<float> aHls =
Convert_sRGB_To_HLS(NCollection_Vec3<float>((float)theR, (float)theG, (float)theB));
public:
//! Returns the value used to compare two colors for equality; 0.0001 by default.
- Standard_EXPORT static Standard_Real Epsilon();
+ Standard_EXPORT static Standard_Real Epsilon() noexcept;
//! Set the value used to compare two colors for equality.
- Standard_EXPORT static void SetEpsilon(const Standard_Real theEpsilon);
+ Standard_EXPORT static void SetEpsilon(const Standard_Real theEpsilon) noexcept;
//! Dumps the content of me into the stream
Standard_EXPORT void DumpJson(Standard_OStream& theOStream, Standard_Integer theDepth = -1) const;
HexColorLength_RGBA = 8 //!< RGBA hex color format
};
+static constexpr ColorInteger HEX_BASE = 16; // Hexadecimal number base
+static constexpr int HEX_BITS_PER_COMPONENT = 8; // 8 bits per component (256 values)
+static constexpr int HEX_BITS_PER_COMPONENT_SHORT = 4; // 4 bits per component (16 values)
+static constexpr int RGB_COMPONENT_LAST_INDEX = 2; // Last RGB component index (B in RGB)
+
//! Takes next color component from the integer representing a color (it is a step in a process of a
//! conversion implemented by the function ConvertIntegerToColorRGBA)
//! @param theColorInteger the integer representing a color
takeColorComponentFromInteger(theColorInteger, theColorComponentBase);
aColor.a() = anAlphaComponent;
}
- for (Standard_Integer aColorComponentIndex = 2; aColorComponentIndex >= 0; --aColorComponentIndex)
+ for (Standard_Integer aColorComponentIndex = RGB_COMPONENT_LAST_INDEX; aColorComponentIndex >= 0;
+ --aColorComponentIndex)
{
const Standard_ShortReal aColorComponent =
takeColorComponentFromInteger(theColorInteger, theColorComponentBase);
}
ColorInteger aHexColorInteger;
- if (!convertStringToInteger(aHexColorString, aHexColorInteger, 16u))
+ if (!convertStringToInteger(aHexColorString, aHexColorInteger, HEX_BASE))
{
return false;
}
return false;
}
- const ColorInteger THE_HEX_COLOR_COMPONENT_BASE = 1 << 8;
- const ColorInteger THE_HEX_COLOR_COMPONENT_SHORT_BASE = 1 << 4;
+ const ColorInteger THE_HEX_COLOR_COMPONENT_BASE = 1 << HEX_BITS_PER_COMPONENT;
+ const ColorInteger THE_HEX_COLOR_COMPONENT_SHORT_BASE = 1 << HEX_BITS_PER_COMPONENT_SHORT;
const ColorInteger aColorComponentBase =
isShort ? THE_HEX_COLOR_COMPONENT_SHORT_BASE : THE_HEX_COLOR_COMPONENT_BASE;
return convertIntegerToColorRGBA(aHexColorInteger,
}
//! Creates the color with specified RGB value.
- explicit Quantity_ColorRGBA(const Quantity_Color& theRgb)
+ constexpr explicit Quantity_ColorRGBA(const Quantity_Color& theRgb)
: myRgb(theRgb),
myAlpha(1.0f)
{
}
//! Creates the color with specified RGBA values.
- Quantity_ColorRGBA(const Quantity_Color& theRgb, float theAlpha)
+ constexpr Quantity_ColorRGBA(const Quantity_Color& theRgb, float theAlpha)
: myRgb(theRgb),
myAlpha(theAlpha)
{
}
//! Assign new values to the color.
- void SetValues(float theRed, float theGreen, float theBlue, float theAlpha)
+ void SetValues(float theRed, float theGreen, float theBlue, float theAlpha) noexcept
{
myRgb.SetValues(theRed, theGreen, theBlue, Quantity_TOC_RGB);
myAlpha = theAlpha;
}
//! Return RGB color value.
- const Quantity_Color& GetRGB() const { return myRgb; }
+ constexpr const Quantity_Color& GetRGB() const noexcept { return myRgb; }
//! Modify RGB color components without affecting alpha value.
- Quantity_Color& ChangeRGB() { return myRgb; }
+ constexpr Quantity_Color& ChangeRGB() noexcept { return myRgb; }
//! Assign RGB color components without affecting alpha value.
- void SetRGB(const Quantity_Color& theRgb) { myRgb = theRgb; }
+ constexpr void SetRGB(const Quantity_Color& theRgb) noexcept { myRgb = theRgb; }
//! Return alpha value (1.0 means opaque, 0.0 means fully transparent).
- Standard_ShortReal Alpha() const { return myAlpha; }
+ constexpr Standard_ShortReal Alpha() const noexcept { return myAlpha; }
//! Assign the alpha value.
- void SetAlpha(const Standard_ShortReal theAlpha) { myAlpha = theAlpha; }
+ constexpr void SetAlpha(const Standard_ShortReal theAlpha) noexcept { myAlpha = theAlpha; }
//! Return the color as vector of 4 float elements.
- operator const NCollection_Vec4<float>&() const { return *(const NCollection_Vec4<float>*)this; }
+ constexpr operator const NCollection_Vec4<float>&() const noexcept
+ {
+ return *(const NCollection_Vec4<float>*)this;
+ }
//! Returns true if the distance between colors is greater than Epsilon().
- bool IsDifferent(const Quantity_ColorRGBA& theOther) const
+ bool IsDifferent(const Quantity_ColorRGBA& theOther) const noexcept
{
return myRgb.IsDifferent(theOther.GetRGB())
|| Abs(myAlpha - theOther.myAlpha) > (float)Quantity_Color::Epsilon();
}
//! Returns true if the distance between colors is greater than Epsilon().
- bool operator!=(const Quantity_ColorRGBA& theOther) const { return IsDifferent(theOther); }
+ bool operator!=(const Quantity_ColorRGBA& theOther) const noexcept
+ {
+ return IsDifferent(theOther);
+ }
//! Two colors are considered to be equal if their distance is no greater than Epsilon().
- bool IsEqual(const Quantity_ColorRGBA& theOther) const
+ bool IsEqual(const Quantity_ColorRGBA& theOther) const noexcept
{
return myRgb.IsEqual(theOther.GetRGB())
&& Abs(myAlpha - theOther.myAlpha) <= (float)Quantity_Color::Epsilon();
}
//! Two colors are considered to be equal if their distance is no greater than Epsilon().
- bool operator==(const Quantity_ColorRGBA& theOther) const { return IsEqual(theOther); }
+ bool operator==(const Quantity_ColorRGBA& theOther) const noexcept { return IsEqual(theOther); }
public:
//! Finds color from predefined names.
//! @param theColor a found color
//! @return false if the color name is unknown, or true if the search by color name was successful
static Standard_Boolean ColorFromName(const Standard_CString theColorNameString,
- Quantity_ColorRGBA& theColor)
+ Quantity_ColorRGBA& theColor) noexcept
{
Quantity_ColorRGBA aColor;
if (!Quantity_Color::ColorFromName(theColorNameString, aColor.ChangeRGB()))
//! Returns hex sRGBA string in format "#RRGGBBAA".
static TCollection_AsciiString ColorToHex(const Quantity_ColorRGBA& theColor,
- const bool theToPrefixHash = true)
+ const bool theToPrefixHash = true) noexcept
{
NCollection_Vec4<Standard_ShortReal> anSRgb =
Convert_LinearRGB_To_sRGB((NCollection_Vec4<Standard_ShortReal>)theColor);
public:
//! Convert linear RGB components into sRGB using OpenGL specs formula.
- static NCollection_Vec4<float> Convert_LinearRGB_To_sRGB(const NCollection_Vec4<float>& theRGB)
+ static NCollection_Vec4<float> Convert_LinearRGB_To_sRGB(
+ const NCollection_Vec4<float>& theRGB) noexcept
{
return NCollection_Vec4<float>(Quantity_Color::Convert_LinearRGB_To_sRGB(theRGB.r()),
Quantity_Color::Convert_LinearRGB_To_sRGB(theRGB.g()),
}
//! Convert sRGB components into linear RGB using OpenGL specs formula.
- static NCollection_Vec4<float> Convert_sRGB_To_LinearRGB(const NCollection_Vec4<float>& theRGB)
+ static NCollection_Vec4<float> Convert_sRGB_To_LinearRGB(
+ const NCollection_Vec4<float>& theRGB) noexcept
{
return NCollection_Vec4<float>(Quantity_Color::Convert_sRGB_To_LinearRGB(theRGB.r()),
Quantity_Color::Convert_sRGB_To_LinearRGB(theRGB.g()),
#include <Quantity_Period.hxx>
#include <Standard_OutOfRange.hxx>
-static int month_table[12] = {31, // January
- 28, // February
- 31, // March
- 30, // April
- 31, // May
- 30, // June
- 31, // July
- 31, // August
- 30, // September
- 31, // October
- 30, // November
- 31}; // December
-
-static int SecondsByYear = 365 * 24 * 3600; // Normal Year
-static int SecondsByLeapYear = 366 * 24 * 3600; // Leap Year
+#include "Quantity_TimeConstants.pxx"
+
+namespace
+{
+
+static constexpr int month_table[12] = {31, // January
+ 28, // February
+ 31, // March
+ 30, // April
+ 31, // May
+ 30, // June
+ 31, // July
+ 31, // August
+ 30, // September
+ 31, // October
+ 30, // November
+ 31}; // December
+
+static constexpr int SecondsByYear = 365 * SECONDS_PER_DAY; // Normal Year
+static constexpr int SecondsByLeapYear = 366 * SECONDS_PER_DAY; // Leap Year
+
+// Returns the number of days in a month for a given year (handles leap years)
+constexpr Standard_Integer getDaysInMonth(const Standard_Integer theMonth,
+ const Standard_Integer theYear) noexcept
+{
+ if (theMonth == 2)
+ {
+ return Quantity_Date::IsLeap(theYear) ? 29 : 28;
+ }
+ return month_table[theMonth - 1];
+}
+} // anonymous namespace
// -----------------------------------------
// Initialize a date to January,1 1979 00:00
if (yy < 1979)
return Standard_False;
- if (Quantity_Date::IsLeap(yy))
- month_table[1] = 29;
- else
- month_table[1] = 28;
-
- if (dd < 1 || dd > month_table[mm - 1])
+ if (dd < 1 || dd > getDaysInMonth(mm, yy))
return Standard_False;
if (hh < 0 || hh > 23)
if (!Quantity_Date::IsValid(mm, dd, yy, hh, mn, ss, mis, mics))
throw Quantity_DateDefinitionError("Quantity_Date::Quantity_Date invalid parameters");
- if (Quantity_Date::IsLeap(yy))
- month_table[1] = 29;
- else
- month_table[1] = 28;
-
mySec = 0;
myUSec = 0;
for (i = 1979; i < yy; i++)
for (i = 1; i < mm; i++)
{
- mySec += month_table[i - 1] * 3600 * 24;
+ mySec += getDaysInMonth(i, yy) * SECONDS_PER_DAY;
}
- mySec += 3600 * 24 * (dd - 1);
+ mySec += SECONDS_PER_DAY * (dd - 1);
- mySec += 3600 * hh;
+ mySec += SECONDS_PER_HOUR * hh;
- mySec += 60 * mn;
+ mySec += SECONDS_PER_MINUTE * mn;
mySec += ss;
- myUSec += mis * 1000;
+ myUSec += mis * USECS_PER_MSEC;
myUSec += mics;
}
Standard_Integer& mics) const
{
- Standard_Integer i, carry;
+ Standard_Integer carry;
for (yy = 1979, carry = mySec;; yy++)
{
if (!Quantity_Date::IsLeap(yy))
{
- month_table[1] = 28; // normal year
if (carry >= SecondsByYear)
carry -= SecondsByYear;
else
}
else
{
- month_table[1] = 29; // Leap year
if (carry >= SecondsByLeapYear)
carry -= SecondsByLeapYear;
else
for (mm = 1;; mm++)
{
- i = month_table[mm - 1] * 3600 * 24;
+ Standard_Integer i = getDaysInMonth(mm, yy) * SECONDS_PER_DAY;
if (carry >= i)
carry -= i;
else
break;
}
- i = 3600 * 24;
- for (dd = 1;; dd++)
- {
- if (carry >= i)
- carry -= i;
- else
- break;
- }
-
- for (hh = 0;; hh++)
- {
- if (carry >= 3600)
- carry -= 3600;
- else
- break;
- }
+ // Extract day within the month
+ // carry holds seconds since the beginning of the current month
+ dd = carry / SECONDS_PER_DAY + 1; // Convert 0-based to 1-based day
+ carry -= (dd - 1) * SECONDS_PER_DAY; // Remove day component from carry
- for (mn = 0;; mn++)
- {
- if (carry >= 60)
- carry -= 60;
- else
- break;
- }
-
- ss = carry;
-
- mis = myUSec / 1000;
- mics = myUSec - (mis * 1000);
+ extractTimeComponents(carry, hh, mn, ss);
+ extractMillisAndMicros(myUSec, mis, mics);
}
// ---------------------------------------------------------------------
Quantity_Period Quantity_Date::Difference(const Quantity_Date& OtherDate)
{
-
Standard_Integer i1, i2;
+ // Special case: if this date is the epoch (Jan 1, 1979 00:00),
+ // return OtherDate as a period (time elapsed since epoch)
if (mySec == 0 && myUSec == 0)
{
i1 = OtherDate.mySec;
i2 = myUSec - OtherDate.myUSec;
}
- if (i1 >= 0 && i2 < 0)
- {
- i1--;
- i2 = 1000000 + i2;
- }
- else if (i1 < 0 && i2 >= 0)
+ // Normalize: handle microsecond underflow
+ normalizeSubtractionBorrow(i1, i2);
+
+ // Period is always absolute value, convert negative result
+ if (i1 < 0)
{
- i1 = Abs(i1);
+ i1 = -i1;
if (i2 > 0)
{
i1--;
- i2 = 1000000 - i2;
+ i2 = USECS_PER_SEC - i2;
}
}
- else if (i1 < 0 && i2 < 0)
- {
- i1 = Abs(i1);
- i2 = Abs(i2);
- }
Quantity_Period result(i1, i2);
-
return (result);
}
result.mySec -= ss;
result.myUSec -= mics;
- if (result.mySec >= 0 && result.myUSec < 0)
- {
- result.mySec--;
- result.myUSec = 1000000 + result.myUSec;
- }
+ normalizeSubtractionBorrow(result.mySec, result.myUSec);
if (result.mySec < 0)
throw Quantity_DateDefinitionError(
During.Values(result.mySec, result.myUSec);
result.mySec += mySec;
result.myUSec += myUSec;
- if (result.myUSec >= 1000000)
- {
- result.mySec++;
- result.myUSec -= 1000000;
- }
+ normalizeAdditionOverflow(result.mySec, result.myUSec);
return (result);
}
// ----------------------------------------------------------------------
Standard_Integer Quantity_Date::Year()
{
- Standard_Integer dummy, year;
- Values(dummy, dummy, year, dummy, dummy, dummy, dummy, dummy);
+ Standard_Integer mm, dd, year, hh, mn, ss, mis, mics;
+ Values(mm, dd, year, hh, mn, ss, mis, mics);
return (year);
}
// ----------------------------------------------------------------------
Standard_Integer Quantity_Date::Month()
{
- Standard_Integer dummy, month;
- Values(month, dummy, dummy, dummy, dummy, dummy, dummy, dummy);
- return (month);
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ Values(mm, dd, yy, hh, mn, ss, mis, mics);
+ return (mm);
}
// ----------------------------------------------------------------------
Standard_Integer Quantity_Date::Day()
{
- Standard_Integer dummy, day;
- Values(dummy, day, dummy, dummy, dummy, dummy, dummy, dummy);
- return (day);
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ Values(mm, dd, yy, hh, mn, ss, mis, mics);
+ return (dd);
}
// ----------------------------------------------------------------------
Standard_Integer Quantity_Date::Hour()
{
- Standard_Integer dummy, hour;
- Values(dummy, dummy, dummy, hour, dummy, dummy, dummy, dummy);
- return (hour);
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ Values(mm, dd, yy, hh, mn, ss, mis, mics);
+ return (hh);
}
// ----------------------------------------------------------------------
Standard_Integer Quantity_Date::Minute()
{
- Standard_Integer dummy, min;
- Values(dummy, dummy, dummy, dummy, min, dummy, dummy, dummy);
- return (min);
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ Values(mm, dd, yy, hh, mn, ss, mis, mics);
+ return (mn);
}
// ----------------------------------------------------------------------
Standard_Integer Quantity_Date::Second()
{
- Standard_Integer dummy, sec;
- Values(dummy, dummy, dummy, dummy, dummy, sec, dummy, dummy);
- return (sec);
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ Values(mm, dd, yy, hh, mn, ss, mis, mics);
+ return (ss);
}
// ----------------------------------------------------------------------
Standard_Integer Quantity_Date::MilliSecond()
{
- Standard_Integer dummy, msec;
- Values(dummy, dummy, dummy, dummy, dummy, dummy, msec, dummy);
- return (msec);
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ Values(mm, dd, yy, hh, mn, ss, mis, mics);
+ return (mis);
}
// ----------------------------------------------------------------------
Standard_Integer Quantity_Date::MicroSecond()
{
- Standard_Integer dummy, msec;
- Values(dummy, dummy, dummy, dummy, dummy, dummy, dummy, msec);
- return (msec);
-}
-
-// ----------------------------------------------------------------------
-// IsEarlier : Return true if the date is earlier than an other date
-// ~~~~~~~~~
-// ----------------------------------------------------------------------
-
-Standard_Boolean Quantity_Date::IsEarlier(const Quantity_Date& other) const
-{
- if (mySec < other.mySec)
- return Standard_True;
- else if (mySec > other.mySec)
- return Standard_False;
- else
- return ((myUSec < other.myUSec) ? Standard_True : Standard_False);
-}
-
-// ----------------------------------------------------------------------
-// IsLater : Return true if the date is later than an other date
-// ~~~~~~~
-// ----------------------------------------------------------------------
-
-Standard_Boolean Quantity_Date::IsLater(const Quantity_Date& other) const
-{
- if (mySec > other.mySec)
- return Standard_True;
- else if (mySec < other.mySec)
- return Standard_False;
- else
- return ((myUSec > other.myUSec) ? Standard_True : Standard_False);
-}
-
-// ----------------------------------------------------------------------
-// IsEqual : Return true if the date is the same than an other date
-// ~~~~~~~
-// ----------------------------------------------------------------------
-
-Standard_Boolean Quantity_Date::IsEqual(const Quantity_Date& other) const
-{
- return ((myUSec == other.myUSec && mySec == other.mySec) ? Standard_True : Standard_False);
+ Standard_Integer mm, dd, yy, hh, mn, ss, mis, mics;
+ Values(mm, dd, yy, hh, mn, ss, mis, mics);
+ return (mics);
}
//! Returns TRUE if both <me> and <other> are equal.
//! This method is an alias of operator ==.
- Standard_EXPORT Standard_Boolean IsEqual(const Quantity_Date& anOther) const;
+ constexpr Standard_Boolean IsEqual(const Quantity_Date& anOther) const noexcept
+ {
+ return (myUSec == anOther.myUSec && mySec == anOther.mySec);
+ }
- Standard_Boolean operator==(const Quantity_Date& anOther) const { return IsEqual(anOther); }
+ constexpr Standard_Boolean operator==(const Quantity_Date& anOther) const noexcept
+ {
+ return IsEqual(anOther);
+ }
//! Returns TRUE if <me> is earlier than <other>.
- Standard_EXPORT Standard_Boolean IsEarlier(const Quantity_Date& anOther) const;
+ constexpr Standard_Boolean IsEarlier(const Quantity_Date& anOther) const noexcept
+ {
+ return (mySec < anOther.mySec) || (mySec == anOther.mySec && myUSec < anOther.myUSec);
+ }
- Standard_Boolean operator<(const Quantity_Date& anOther) const { return IsEarlier(anOther); }
+ constexpr Standard_Boolean operator<(const Quantity_Date& anOther) const noexcept
+ {
+ return IsEarlier(anOther);
+ }
//! Returns TRUE if <me> is later then <other>.
- Standard_EXPORT Standard_Boolean IsLater(const Quantity_Date& anOther) const;
+ constexpr Standard_Boolean IsLater(const Quantity_Date& anOther) const noexcept
+ {
+ return (mySec > anOther.mySec) || (mySec == anOther.mySec && myUSec > anOther.myUSec);
+ }
- Standard_Boolean operator>(const Quantity_Date& anOther) const { return IsLater(anOther); }
+ constexpr Standard_Boolean operator>(const Quantity_Date& anOther) const noexcept
+ {
+ return IsLater(anOther);
+ }
//! Checks the validity of a date - returns true if a
//! date defined from the year yyyy, the month mm,
//! Returns true if a year is a leap year.
//! The leap years are divisible by 4 and not by 100 except
//! the years divisible by 400.
- static Standard_Boolean IsLeap(const Standard_Integer yy)
+ static constexpr Standard_Boolean IsLeap(const Standard_Integer yy) noexcept
{
return ((yy % 4 == 0) && (yy % 100 != 0)) || (yy % 400) == 0;
}
#include <Quantity_Period.hxx>
#include <Quantity_PeriodDefinitionError.hxx>
+#include "Quantity_TimeConstants.pxx"
+
// -----------------------------------------------------------
// IsValid : Checks the validity of a date
// With:
const Standard_Integer mis,
const Standard_Integer mics)
{
-
- return ((dd < 0 || hh < 0 || mn < 0 || ss < 0 || mis < 0 || mics < 0) ? Standard_False
- : Standard_True);
+ return (dd >= 0 && hh >= 0 && mn >= 0 && ss >= 0 && mis >= 0 && mics >= 0);
}
// -------------------------------------------------------------
// -------------------------------------------------------------
Standard_Boolean Quantity_Period::IsValid(const Standard_Integer ss, const Standard_Integer mics)
{
-
- return ((ss < 0 || mics < 0) ? Standard_False : Standard_True);
+ return (ss >= 0 && mics >= 0);
}
// -------------------------------------------------------------
Standard_Integer& mics) const
{
Standard_Integer carry = mySec;
- dd = carry / (24 * 3600);
- carry -= dd * 24 * 3600;
- hh = carry / 3600;
- carry -= 3600 * hh;
- mn = carry / 60;
- carry -= mn * 60;
- ss = carry;
- mis = myUSec / 1000;
- mics = myUSec - (mis * 1000);
+ dd = carry / SECONDS_PER_DAY;
+ carry -= dd * SECONDS_PER_DAY;
+ extractTimeComponents(carry, hh, mn, ss);
+ extractMillisAndMicros(myUSec, mis, mics);
}
// -------------------------------------------------------------
const Standard_Integer mils,
const Standard_Integer mics)
{
- SetValues((dd * 24 * 3600) + (hh * 3600) + (60 * mn) + ss, mils * 1000 + mics);
+ SetValues((dd * SECONDS_PER_DAY) + (hh * SECONDS_PER_HOUR) + (SECONDS_PER_MINUTE * mn) + ss,
+ mils * USECS_PER_MSEC + mics);
}
// -------------------------------------------------------------
mySec = ss;
myUSec = mics;
- while (myUSec > 1000000)
- {
- myUSec -= 1000000;
- mySec++;
- }
+ normalizeAdditionOverflow(mySec, myUSec);
}
// -------------------------------------------------------------
result.mySec -= OtherPeriod.mySec;
result.myUSec -= OtherPeriod.myUSec;
- if (result.mySec >= 0 && result.myUSec < 0)
- {
- result.mySec--;
- result.myUSec = 1000000 + result.myUSec;
- }
- else if (result.mySec < 0 && result.myUSec >= 0)
+ normalizeSubtractionBorrow(result.mySec, result.myUSec);
+
+ // Handle negative result (convert to absolute value)
+ // Note: after normalization, myUSec is always in [0, 999999]
+ if (result.mySec < 0)
{
result.mySec = Abs(result.mySec);
if (result.myUSec > 0)
{
result.mySec--;
- result.myUSec = 1000000 - result.myUSec;
+ result.myUSec = USECS_PER_SEC - result.myUSec;
}
}
- else if (result.mySec < 0 && result.myUSec < 0)
- {
- result.mySec = Abs(result.mySec);
- result.myUSec = Abs(result.myUSec);
- }
return (result);
}
Quantity_Period result(mySec, myUSec);
result.mySec += OtherPeriod.mySec;
result.myUSec += OtherPeriod.myUSec;
- if (result.myUSec > 1000000)
- {
- result.myUSec -= 1000000;
- result.mySec++;
- }
+ normalizeAdditionOverflow(result.mySec, result.myUSec);
return (result);
}
-
-// -------------------------------------------------------------
-// IsEqual : returns true if two periods are equal
-// ~~~~~~~
-// -------------------------------------------------------------
-Standard_Boolean Quantity_Period::IsEqual(const Quantity_Period& OtherPeriod) const
-{
-
- return ((mySec == OtherPeriod.mySec && myUSec == OtherPeriod.myUSec) ? Standard_True
- : Standard_False);
-}
-
-// -------------------------------------------------------------
-// IsShorter : returns true if a date is shorter then an other
-// ~~~~~~~~~ date
-// -------------------------------------------------------------
-Standard_Boolean Quantity_Period::IsShorter(const Quantity_Period& OtherPeriod) const
-{
-
- if (mySec < OtherPeriod.mySec)
- return Standard_True;
- else if (mySec > OtherPeriod.mySec)
- return Standard_False;
- else
- return ((myUSec < OtherPeriod.myUSec) ? Standard_True : Standard_False);
-}
-
-// -------------------------------------------------------------
-// IsLonger : returns true if a date is longer then an other
-// ~~~~~~~~ date
-// -------------------------------------------------------------
-Standard_Boolean Quantity_Period::IsLonger(const Quantity_Period& OtherPeriod) const
-{
-
- if (mySec > OtherPeriod.mySec)
- return Standard_True;
- else if (mySec < OtherPeriod.mySec)
- return Standard_False;
- else
- return ((myUSec > OtherPeriod.myUSec) ? Standard_True : Standard_False);
-}
Quantity_Period operator+(const Quantity_Period& anOther) const { return Add(anOther); }
//! Returns TRUE if both <me> and <other> are equal.
- Standard_EXPORT Standard_Boolean IsEqual(const Quantity_Period& anOther) const;
+ constexpr Standard_Boolean IsEqual(const Quantity_Period& anOther) const noexcept
+ {
+ return (mySec == anOther.mySec && myUSec == anOther.myUSec);
+ }
- Standard_Boolean operator==(const Quantity_Period& anOther) const { return IsEqual(anOther); }
+ constexpr Standard_Boolean operator==(const Quantity_Period& anOther) const noexcept
+ {
+ return IsEqual(anOther);
+ }
//! Returns TRUE if <me> is shorter than <other>.
- Standard_EXPORT Standard_Boolean IsShorter(const Quantity_Period& anOther) const;
+ constexpr Standard_Boolean IsShorter(const Quantity_Period& anOther) const noexcept
+ {
+ return (mySec < anOther.mySec) || (mySec == anOther.mySec && myUSec < anOther.myUSec);
+ }
- Standard_Boolean operator<(const Quantity_Period& anOther) const { return IsShorter(anOther); }
+ constexpr Standard_Boolean operator<(const Quantity_Period& anOther) const noexcept
+ {
+ return IsShorter(anOther);
+ }
//! Returns TRUE if <me> is longer then <other>.
- Standard_EXPORT Standard_Boolean IsLonger(const Quantity_Period& anOther) const;
-
- Standard_Boolean operator>(const Quantity_Period& anOther) const { return IsLonger(anOther); }
+ constexpr Standard_Boolean IsLonger(const Quantity_Period& anOther) const noexcept
+ {
+ return (mySec > anOther.mySec) || (mySec == anOther.mySec && myUSec > anOther.myUSec);
+ }
+
+ constexpr Standard_Boolean operator>(const Quantity_Period& anOther) const noexcept
+ {
+ return IsLonger(anOther);
+ }
//! Checks the validity of a Period in form (dd,hh,mn,ss,mil,mic)
//! With:
--- /dev/null
+// Copyright (c) 2025 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Quantity_TimeConstants_HeaderFile
+#define _Quantity_TimeConstants_HeaderFile
+
+//! @file Quantity_TimeConstants.pxx
+//! Internal header providing shared time conversion constants and helper functions
+//! for Quantity_Date and Quantity_Period classes.
+
+#include <Standard_Integer.hxx>
+
+namespace
+{
+
+//! Time conversion constants
+constexpr int SECONDS_PER_MINUTE = 60;
+constexpr int SECONDS_PER_HOUR = 3600; // 60 * 60
+constexpr int SECONDS_PER_DAY = 86400; // 24 * 3600
+
+//! Microsecond conversion constants
+constexpr int USECS_PER_MSEC = 1000; // microseconds per millisecond
+constexpr int USECS_PER_SEC = 1000000; // microseconds per second
+
+//! Extracts milliseconds and remaining microseconds from total microseconds
+//! @param theUSec total microseconds
+//! @param theMis output: milliseconds part
+//! @param theMics output: remaining microseconds part
+inline void extractMillisAndMicros(const Standard_Integer theUSec,
+ Standard_Integer& theMis,
+ Standard_Integer& theMics) noexcept
+{
+ theMis = theUSec / USECS_PER_MSEC;
+ theMics = theUSec - (theMis * USECS_PER_MSEC);
+}
+
+//! Extracts hours, minutes, and seconds from remaining seconds in a day
+//! @param theCarry input/output: seconds to extract from, updated with remainder
+//! @param theHH output: hours
+//! @param theMN output: minutes
+//! @param theSS output: seconds
+inline void extractTimeComponents(Standard_Integer& theCarry,
+ Standard_Integer& theHH,
+ Standard_Integer& theMN,
+ Standard_Integer& theSS) noexcept
+{
+ theHH = theCarry / SECONDS_PER_HOUR;
+ theCarry -= SECONDS_PER_HOUR * theHH;
+ theMN = theCarry / SECONDS_PER_MINUTE;
+ theCarry -= theMN * SECONDS_PER_MINUTE;
+ theSS = theCarry;
+}
+
+//! Normalizes time values when microseconds overflow into seconds
+//! (handles addition overflow: myUSec >= 1000000)
+//! Uses division for O(1) complexity
+//! @param theSec input/output: seconds, incremented if overflow occurs
+//! @param theUSec input/output: microseconds, normalized to 0..999999
+inline void normalizeAdditionOverflow(Standard_Integer& theSec, Standard_Integer& theUSec) noexcept
+{
+ if (theUSec >= USECS_PER_SEC)
+ {
+ const Standard_Integer overflow = theUSec / USECS_PER_SEC;
+ theSec += overflow;
+ theUSec -= overflow * USECS_PER_SEC;
+ }
+}
+
+//! Normalizes time values when microseconds require borrowing from seconds
+//! (handles subtraction borrow: myUSec < 0)
+//! Uses ceiling division for O(1) complexity
+//! @param theSec input/output: seconds, decremented if borrow occurs
+//! @param theUSec input/output: microseconds, normalized to 0..999999
+inline void normalizeSubtractionBorrow(Standard_Integer& theSec, Standard_Integer& theUSec) noexcept
+{
+ if (theUSec < 0)
+ {
+ const Standard_Integer borrow = (-theUSec + USECS_PER_SEC - 1) / USECS_PER_SEC;
+ theSec -= borrow;
+ theUSec += borrow * USECS_PER_SEC;
+ }
+}
+
+} // anonymous namespace
+
+#endif // _Quantity_TimeConstants_HeaderFile