mirror of
https://github.com/Open-Cascade-SAS/OCCT.git
synced 2026-05-10 09:30:48 +08:00
Foundation Classes - Optimize Quantity package (#834)
- Converted validation macros to inline functions for better type safety - Added `noexcept` specifiers to non-throwing functions for compiler optimization opportunities - Added `constexpr` to compile-time evaluable functions (comparison operators, leap year calculation) - Enhanced `Quantity_Color::StringName()` to return "UNDEFINED" instead of throwing exceptions - Introduced shared time constants header for better maintainability
This commit is contained in:
@@ -22,6 +22,10 @@ set(OCCT_TKernel_GTests_FILES
|
||||
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
|
||||
|
||||
285
src/FoundationClasses/TKernel/GTests/Quantity_ColorRGBA_Test.cxx
Normal file
285
src/FoundationClasses/TKernel/GTests/Quantity_ColorRGBA_Test.cxx
Normal file
@@ -0,0 +1,285 @@
|
||||
// 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));
|
||||
}
|
||||
293
src/FoundationClasses/TKernel/GTests/Quantity_Color_Test.cxx
Normal file
293
src/FoundationClasses/TKernel/GTests/Quantity_Color_Test.cxx
Normal file
@@ -0,0 +1,293 @@
|
||||
// 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
|
||||
}
|
||||
409
src/FoundationClasses/TKernel/GTests/Quantity_Date_Test.cxx
Normal file
409
src/FoundationClasses/TKernel/GTests/Quantity_Date_Test.cxx
Normal file
@@ -0,0 +1,409 @@
|
||||
// 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);
|
||||
}
|
||||
393
src/FoundationClasses/TKernel/GTests/Quantity_Period_Test.cxx
Normal file
393
src/FoundationClasses/TKernel/GTests/Quantity_Period_Test.cxx
Normal file
@@ -0,0 +1,393 @@
|
||||
// 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);
|
||||
}
|
||||
@@ -21,38 +21,91 @@
|
||||
#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
|
||||
{
|
||||
@@ -64,10 +117,10 @@ struct Quantity_StandardColor
|
||||
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),
|
||||
@@ -85,22 +138,22 @@ struct Quantity_StandardColor
|
||||
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;
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
@@ -132,11 +185,11 @@ NCollection_Vec3<float> Quantity_Color::valuesOf(const Quantity_NameOfColor theN
|
||||
|
||||
//=================================================================================================
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -144,7 +197,7 @@ Standard_CString Quantity_Color::StringName(const Quantity_NameOfColor theName)
|
||||
//=================================================================================================
|
||||
|
||||
Standard_Boolean Quantity_Color::ColorFromName(const Standard_CString theName,
|
||||
Quantity_NameOfColor& theColor)
|
||||
Quantity_NameOfColor& theColor) noexcept
|
||||
{
|
||||
TCollection_AsciiString aName(theName);
|
||||
aName.UpperCase();
|
||||
@@ -249,7 +302,7 @@ Quantity_Color::Quantity_Color(const Standard_Real theC1,
|
||||
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());
|
||||
}
|
||||
|
||||
//=================================================================================================
|
||||
@@ -286,31 +339,31 @@ void Quantity_Color::SetValues(const Standard_Real theC1,
|
||||
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;
|
||||
@@ -346,21 +399,20 @@ Standard_Real Quantity_Color::DeltaE2000(const Quantity_Color& theOther) const
|
||||
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.)
|
||||
@@ -376,13 +428,13 @@ Standard_Real Quantity_Color::DeltaE2000(const Quantity_Color& theOther) const
|
||||
// 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);
|
||||
@@ -391,8 +443,8 @@ Standard_Real Quantity_Color::DeltaE2000(const Quantity_Color& theOther) const
|
||||
|
||||
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;
|
||||
@@ -523,7 +575,8 @@ NCollection_Vec3<float> Quantity_Color::Convert_HLS_To_sRGB(const NCollection_Ve
|
||||
// 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();
|
||||
@@ -573,10 +626,10 @@ NCollection_Vec3<float> Quantity_Color::Convert_sRGB_To_HLS(const NCollection_Ve
|
||||
// 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;
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
@@ -584,10 +637,10 @@ static inline double CIELab_f(double theValue)
|
||||
// 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;
|
||||
}
|
||||
|
||||
// =======================================================================
|
||||
@@ -596,7 +649,7 @@ static inline double CIELab_invertf(double theValue)
|
||||
// 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];
|
||||
@@ -604,18 +657,18 @@ NCollection_Vec3<float> Quantity_Color::Convert_LinearRGB_To_Lab(
|
||||
|
||||
// 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);
|
||||
}
|
||||
@@ -626,7 +679,7 @@ NCollection_Vec3<float> Quantity_Color::Convert_LinearRGB_To_Lab(
|
||||
// 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];
|
||||
@@ -643,19 +696,19 @@ NCollection_Vec3<float> Quantity_Color::Convert_Lab_To_LinearRGB(
|
||||
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.))
|
||||
@@ -670,13 +723,14 @@ NCollection_Vec3<float> Quantity_Color::Convert_Lab_To_LinearRGB(
|
||||
// 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.;
|
||||
@@ -689,12 +743,13 @@ NCollection_Vec3<float> Quantity_Color::Convert_Lab_To_Lch(const NCollection_Vec
|
||||
// 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);
|
||||
|
||||
@@ -65,16 +65,16 @@ public:
|
||||
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.
|
||||
@@ -91,21 +91,21 @@ public:
|
||||
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.
|
||||
@@ -114,7 +114,7 @@ public:
|
||||
|
||||
//! 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.
|
||||
@@ -122,29 +122,32 @@ public:
|
||||
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))
|
||||
@@ -152,7 +155,7 @@ public:
|
||||
}
|
||||
|
||||
//! 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))
|
||||
@@ -186,20 +189,20 @@ public:
|
||||
}
|
||||
|
||||
//! 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))
|
||||
@@ -223,7 +226,7 @@ public:
|
||||
|
||||
//! 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);
|
||||
@@ -240,40 +243,42 @@ public:
|
||||
|
||||
//! 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.
|
||||
@@ -281,7 +286,8 @@ public:
|
||||
//! 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),
|
||||
@@ -293,7 +299,7 @@ public:
|
||||
//! 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),
|
||||
@@ -307,7 +313,7 @@ public:
|
||||
|
||||
//! 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;
|
||||
@@ -315,7 +321,7 @@ public:
|
||||
|
||||
//! 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;
|
||||
@@ -323,7 +329,7 @@ public:
|
||||
|
||||
//! 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);
|
||||
@@ -331,7 +337,7 @@ public:
|
||||
|
||||
//! 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);
|
||||
@@ -339,7 +345,7 @@ public:
|
||||
|
||||
//! 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()),
|
||||
@@ -348,7 +354,7 @@ public:
|
||||
|
||||
//! 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()),
|
||||
@@ -356,20 +362,20 @@ public:
|
||||
}
|
||||
|
||||
//! 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()),
|
||||
@@ -378,7 +384,7 @@ public:
|
||||
|
||||
//! 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()),
|
||||
@@ -391,7 +397,7 @@ public:
|
||||
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));
|
||||
@@ -406,7 +412,7 @@ public:
|
||||
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));
|
||||
@@ -417,10 +423,10 @@ public:
|
||||
|
||||
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;
|
||||
|
||||
@@ -35,6 +35,11 @@ enum HexColorLength
|
||||
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
|
||||
@@ -77,7 +82,8 @@ static bool convertIntegerToColorRGBA(ColorInteger theColorInteger,
|
||||
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);
|
||||
@@ -164,7 +170,7 @@ bool Quantity_ColorRGBA::ColorFromHex(const char* const theHexColorString,
|
||||
}
|
||||
|
||||
ColorInteger aHexColorInteger;
|
||||
if (!convertStringToInteger(aHexColorString, aHexColorInteger, 16u))
|
||||
if (!convertStringToInteger(aHexColorString, aHexColorInteger, HEX_BASE))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -197,8 +203,8 @@ bool Quantity_ColorRGBA::ColorFromHex(const char* const theHexColorString,
|
||||
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,
|
||||
|
||||
@@ -28,14 +28,14 @@ public:
|
||||
}
|
||||
|
||||
//! 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)
|
||||
{
|
||||
@@ -56,49 +56,55 @@ public:
|
||||
}
|
||||
|
||||
//! 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.
|
||||
@@ -108,7 +114,7 @@ public:
|
||||
//! @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()))
|
||||
@@ -133,7 +139,7 @@ public:
|
||||
|
||||
//! 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);
|
||||
@@ -151,7 +157,8 @@ public:
|
||||
|
||||
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()),
|
||||
@@ -160,7 +167,8 @@ public:
|
||||
}
|
||||
|
||||
//! 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()),
|
||||
|
||||
@@ -22,21 +22,38 @@
|
||||
#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
|
||||
#include "Quantity_TimeConstants.pxx"
|
||||
|
||||
static int SecondsByYear = 365 * 24 * 3600; // Normal Year
|
||||
static int SecondsByLeapYear = 366 * 24 * 3600; // Leap Year
|
||||
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
|
||||
@@ -69,12 +86,7 @@ Standard_Boolean Quantity_Date::IsValid(const Standard_Integer mm,
|
||||
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)
|
||||
@@ -135,11 +147,6 @@ void Quantity_Date::SetValues(const Standard_Integer mm,
|
||||
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++)
|
||||
@@ -152,18 +159,18 @@ void Quantity_Date::SetValues(const Standard_Integer mm,
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -183,13 +190,12 @@ void Quantity_Date::Values(Standard_Integer& mm,
|
||||
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
|
||||
@@ -197,7 +203,6 @@ void Quantity_Date::Values(Standard_Integer& mm,
|
||||
}
|
||||
else
|
||||
{
|
||||
month_table[1] = 29; // Leap year
|
||||
if (carry >= SecondsByLeapYear)
|
||||
carry -= SecondsByLeapYear;
|
||||
else
|
||||
@@ -207,42 +212,20 @@ void Quantity_Date::Values(Standard_Integer& mm,
|
||||
|
||||
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;
|
||||
}
|
||||
// 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 (hh = 0;; hh++)
|
||||
{
|
||||
if (carry >= 3600)
|
||||
carry -= 3600;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
@@ -252,9 +235,10 @@ void Quantity_Date::Values(Standard_Integer& mm,
|
||||
|
||||
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;
|
||||
@@ -266,28 +250,21 @@ Quantity_Period Quantity_Date::Difference(const Quantity_Date& OtherDate)
|
||||
i2 = myUSec - OtherDate.myUSec;
|
||||
}
|
||||
|
||||
if (i1 >= 0 && i2 < 0)
|
||||
// Normalize: handle microsecond underflow
|
||||
normalizeSubtractionBorrow(i1, i2);
|
||||
|
||||
// Period is always absolute value, convert negative result
|
||||
if (i1 < 0)
|
||||
{
|
||||
i1--;
|
||||
i2 = 1000000 + i2;
|
||||
}
|
||||
else if (i1 < 0 && i2 >= 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);
|
||||
}
|
||||
|
||||
@@ -308,11 +285,7 @@ Quantity_Date Quantity_Date::Subtract(const Quantity_Period& During)
|
||||
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(
|
||||
@@ -332,11 +305,7 @@ Quantity_Date Quantity_Date::Add(const Quantity_Period& During)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -346,8 +315,8 @@ Quantity_Date Quantity_Date::Add(const Quantity_Period& During)
|
||||
// ----------------------------------------------------------------------
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -357,9 +326,9 @@ Standard_Integer Quantity_Date::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);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -369,9 +338,9 @@ Standard_Integer Quantity_Date::Month()
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -381,9 +350,9 @@ Standard_Integer Quantity_Date::Day()
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -393,9 +362,9 @@ Standard_Integer Quantity_Date::Hour()
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -405,9 +374,9 @@ Standard_Integer Quantity_Date::Minute()
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -417,9 +386,9 @@ Standard_Integer Quantity_Date::Second()
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
@@ -429,47 +398,7 @@ Standard_Integer Quantity_Date::MilliSecond()
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -146,19 +146,37 @@ public:
|
||||
|
||||
//! 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,
|
||||
@@ -189,7 +207,7 @@ public:
|
||||
//! 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;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <Quantity_Period.hxx>
|
||||
#include <Quantity_PeriodDefinitionError.hxx>
|
||||
|
||||
#include "Quantity_TimeConstants.pxx"
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// IsValid : Checks the validity of a date
|
||||
// With:
|
||||
@@ -38,9 +40,7 @@ Standard_Boolean Quantity_Period::IsValid(const Standard_Integer dd,
|
||||
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);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -51,8 +51,7 @@ Standard_Boolean Quantity_Period::IsValid(const Standard_Integer dd,
|
||||
// -------------------------------------------------------------
|
||||
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);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -98,15 +97,10 @@ void Quantity_Period::Values(Standard_Integer& dd,
|
||||
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);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -131,7 +125,8 @@ void Quantity_Period::SetValues(const Standard_Integer dd,
|
||||
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);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -146,11 +141,7 @@ void Quantity_Period::SetValues(const Standard_Integer ss, const Standard_Intege
|
||||
|
||||
mySec = ss;
|
||||
myUSec = mics;
|
||||
while (myUSec > 1000000)
|
||||
{
|
||||
myUSec -= 1000000;
|
||||
mySec++;
|
||||
}
|
||||
normalizeAdditionOverflow(mySec, myUSec);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------
|
||||
@@ -164,25 +155,19 @@ Quantity_Period Quantity_Period::Subtract(const Quantity_Period& OtherPeriod) co
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -196,51 +181,6 @@ Quantity_Period Quantity_Period::Add(const Quantity_Period& OtherPeriod) const
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -112,19 +112,37 @@ public:
|
||||
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;
|
||||
constexpr Standard_Boolean IsLonger(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 IsLonger(anOther); }
|
||||
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:
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
// 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
|
||||
Reference in New Issue
Block a user