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:
Pasukhin Dmitry
2025-11-17 09:54:17 +00:00
committed by GitHub
parent 07239e2a8b
commit c2375c78a6
14 changed files with 1852 additions and 392 deletions

View File

@@ -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

View 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));
}

View 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
}

View 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);
}

View 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);
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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,

View File

@@ -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()),

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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);
}

View File

@@ -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:

View File

@@ -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