diff --git a/src/App/Color.cpp b/src/App/Color.cpp index 17f6e24c1b..7a6bb6fb87 100644 --- a/src/App/Color.cpp +++ b/src/App/Color.cpp @@ -82,20 +82,38 @@ Color& Color::setPackedValue(uint32_t rgba) uint32_t Color::getPackedValue() const { // clang-format off - return (static_cast(r * 255.0F + 0.5F) << 24 | - static_cast(g * 255.0F + 0.5F) << 16 | - static_cast(b * 255.0F + 0.5F) << 8 | - static_cast(a * 255.0F + 0.5F)); + return (static_cast(std::lround(r * 255.0F)) << 24 | + static_cast(std::lround(g * 255.0F)) << 16 | + static_cast(std::lround(b * 255.0F)) << 8 | + static_cast(std::lround(a * 255.0F))); + // clang-format on +} + +void Color::setPackedRGB(uint32_t rgb) +{ + // clang-format off + this->set(static_cast((rgb >> 24) & 0xff) / 255.0F, + static_cast((rgb >> 16) & 0xff) / 255.0F, + static_cast((rgb >> 8) & 0xff) / 255.0F); + // clang-format on +} + +uint32_t Color::getPackedRGB() const +{ + // clang-format off + return (static_cast(std::lround(r * 255.0F)) << 24 | + static_cast(std::lround(g * 255.0F)) << 16 | + static_cast(std::lround(b * 255.0F)) << 8); // clang-format on } uint32_t Color::getPackedARGB() const { // clang-format off - return (static_cast(a * 255.0F + 0.5F) << 24 | - static_cast(r * 255.0F + 0.5F) << 16 | - static_cast(g * 255.0F + 0.5F) << 8 | - static_cast(b * 255.0F + 0.5F)); + return (static_cast(std::lround(a * 255.0F)) << 24 | + static_cast(std::lround(r * 255.0F)) << 16 | + static_cast(std::lround(g * 255.0F)) << 8 | + static_cast(std::lround(b * 255.0F))); // clang-format on } @@ -113,9 +131,9 @@ std::string Color::asHexString() const { std::stringstream ss; ss << "#" << std::hex << std::uppercase << std::setfill('0') - << std::setw(2) << int(r * 255.0F) - << std::setw(2) << int(g * 255.0F) - << std::setw(2) << int(b * 255.0F); + << std::setw(2) << int(std::lround(r * 255.0F)) + << std::setw(2) << int(std::lround(g * 255.0F)) + << std::setw(2) << int(std::lround(b * 255.0F)); return ss.str(); } diff --git a/src/App/Color.h b/src/App/Color.h index 2cff05d547..bb374ae70d 100644 --- a/src/App/Color.h +++ b/src/App/Color.h @@ -27,6 +27,7 @@ #ifdef __GNUC__ # include #endif +#include #include #include @@ -79,6 +80,14 @@ public: * \sa setPackedValue(). */ uint32_t getPackedValue() const; + /** + * Returns color as a 32 bit packed unsigned int in the form 0xRRGGBB. + */ + uint32_t getPackedRGB() const; + /** + * Sets color as a 32 bit packed unsigned int in the form 0xRRGGBB. + */ + void setPackedRGB(uint32_t); /** * Returns color as a 32 bit packed unsigned int in the form 0xAARRGGBB. */ @@ -119,8 +128,13 @@ public: * */ template - inline T asValue() const { - return(T(int(r*255.0f),int(g*255.0f),int(b*255.0f))); + inline T asValue() const + { + // clang-format off + return(T(int(std::lround(r * 255.0F)), + int(std::lround(g * 255.0F)), + int(std::lround(b * 255.0F)))); + // clang-format on } /** * returns color as hex color "#RRGGBB" diff --git a/src/Gui/ViewProviderGeometryObject.cpp b/src/Gui/ViewProviderGeometryObject.cpp index 6aa19ea99f..a5820c48dd 100644 --- a/src/Gui/ViewProviderGeometryObject.cpp +++ b/src/Gui/ViewProviderGeometryObject.cpp @@ -23,6 +23,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include #include #include #include @@ -70,10 +71,8 @@ const App::PropertyIntegerConstraint::Constraints intPercent = {0, 100, 5}; ViewProviderGeometryObject::ViewProviderGeometryObject() { - ParameterGrp::handle hGrp = - App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - - int initialTransparency = hGrp->GetInt("DefaultShapeTransparency", 0); + App::Material mat = getDefaultMaterial(); + long initialTransparency = toPercent(mat.transparency); static const char* dogroup = "Display Options"; static const char* sgroup = "Selection"; @@ -86,28 +85,6 @@ ViewProviderGeometryObject::ViewProviderGeometryObject() "Set object transparency"); Transparency.setConstraints(&intPercent); - App::Material mat(App::Material::DEFAULT); - // This is handled in the material code when using the object appearance - bool randomColor = hGrp->GetBool("RandomColor", false); - float red {}; - float green {}; - float blue {}; - - if (randomColor) { - auto fMax = (float)RAND_MAX; - red = (float)rand() / fMax; - green = (float)rand() / fMax; - blue = (float)rand() / fMax; - } - else { - // Color = (204, 204, 230) - unsigned long shcol = hGrp->GetUnsigned("DefaultShapeColor", 3435980543UL); - red = ((shcol >> 24) & 0xff) / 255.0F; - green = ((shcol >> 16) & 0xff) / 255.0F; - blue = ((shcol >> 8) & 0xff) / 255.0F; - } - mat.diffuseColor = App::Color(red, green, blue); - ADD_PROPERTY_TYPE(ShapeAppearance, (mat), osgroup, App::Prop_None, "Shape appearrance"); ADD_PROPERTY_TYPE(BoundingBox, (false), dogroup, App::Prop_None, "Display object bounding box"); ADD_PROPERTY_TYPE(Selectable, @@ -116,8 +93,7 @@ ViewProviderGeometryObject::ViewProviderGeometryObject() App::Prop_None, "Set if the object is selectable in the 3d view"); - bool enableSel = hGrp->GetBool("EnableSelection", true); - Selectable.setValue(enableSel); + Selectable.setValue(isSelectionEnabled()); pcShapeMaterial = new SoMaterial; setSoMaterial(mat); @@ -139,6 +115,56 @@ ViewProviderGeometryObject::~ViewProviderGeometryObject() pcBoundColor->unref(); } +App::Material ViewProviderGeometryObject::getDefaultMaterial() const +{ + ParameterGrp::handle hGrp = + App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + + auto getColor = [hGrp](const char* parameter, App::Color& color) { + uint32_t packed = color.getPackedRGB(); + packed = hGrp->GetUnsigned(parameter, packed); + color.setPackedRGB(packed); + }; + auto intRandom = [] (int min, int max) -> int { + static std::mt19937 generator; + std::uniform_int_distribution distribution(min, max); + return distribution(generator); + }; + + App::Material mat(App::Material::DEFAULT); + mat.transparency = fromPercent(hGrp->GetInt("DefaultShapeTransparency", 0)); + long shininess = toPercent(mat.shininess); + mat.shininess = fromPercent(hGrp->GetInt("DefaultShapeShininess", shininess)); + + // This is handled in the material code when using the object appearance + bool randomColor = hGrp->GetBool("RandomColor", false); + + // diffuse color + if (randomColor) { + float red = static_cast(intRandom(0, 255)) / 255.0F; + float green = static_cast(intRandom(0, 255)) / 255.0F; + float blue = static_cast(intRandom(0, 255)) / 255.0F; + mat.diffuseColor = App::Color(red, green, blue); + } + else { + // Color = (204, 204, 230) = 3435980543UL + getColor("DefaultShapeColor", mat.diffuseColor); + } + + getColor("DefaultAmbientColor", mat.ambientColor); + getColor("DefaultEmissiveColor", mat.emissiveColor); + getColor("DefaultSpecularColor", mat.specularColor); + + return mat; +} + +bool ViewProviderGeometryObject::isSelectionEnabled() const +{ + ParameterGrp::handle hGrp = + App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + return hGrp->GetBool("EnableSelection", true); +} + void ViewProviderGeometryObject::onChanged(const App::Property* prop) { // Actually, the properties 'ShapeColor' and 'Transparency' are part of the property @@ -257,8 +283,8 @@ unsigned long ViewProviderGeometryObject::getBoundColor() const { ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); - unsigned long bbcol = - hGrp->GetUnsigned("BoundingBoxColor", 4294967295UL); // white (255,255,255) + // white (255,255,255) + unsigned long bbcol = hGrp->GetUnsigned("BoundingBoxColor", 4294967295UL); return bbcol; } diff --git a/src/Gui/ViewProviderGeometryObject.h b/src/Gui/ViewProviderGeometryObject.h index f968301d62..6b2d745c31 100644 --- a/src/Gui/ViewProviderGeometryObject.h +++ b/src/Gui/ViewProviderGeometryObject.h @@ -104,11 +104,15 @@ protected: virtual unsigned long getBoundColor() const; - void setSoMaterial(const App::Material& source); void handleChangedPropertyName(Base::XMLReader& reader, const char* TypeName, const char* PropName) override; +private: + void setSoMaterial(const App::Material& source); + App::Material getDefaultMaterial() const; + bool isSelectionEnabled() const; + protected: SoMaterial* pcShapeMaterial {nullptr}; SoFCBoundingBox* pcBoundingBox {nullptr}; diff --git a/src/Mod/Material/App/MaterialManager.cpp b/src/Mod/Material/App/MaterialManager.cpp index b4986bc38d..6e03f2720c 100644 --- a/src/Mod/Material/App/MaterialManager.cpp +++ b/src/Mod/Material/App/MaterialManager.cpp @@ -21,6 +21,7 @@ #include "PreCompiled.h" #ifndef _PreComp_ +#include #endif #include @@ -116,30 +117,52 @@ std::shared_ptr MaterialManager::defaultMaterial() ParameterGrp::handle hGrp = App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/View"); + + auto getColor = [hGrp](const char* parameter, App::Color& color) { + uint32_t packed = color.getPackedRGB(); + packed = hGrp->GetUnsigned(parameter, packed); + color.setPackedRGB(packed); + }; + auto intRandom = [] (int min, int max) -> int { + static std::mt19937 generator; + std::uniform_int_distribution distribution(min, max); + return distribution(generator); + }; + + App::Material mat(App::Material::DEFAULT); bool randomColor = hGrp->GetBool("RandomColor", false); - float r, g, b; if (randomColor) { - auto fMax = (float)RAND_MAX; - r = (float)rand() / fMax; - g = (float)rand() / fMax; - b = (float)rand() / fMax; + float red = static_cast(intRandom(0, 255)) / 255.0F; + float green = static_cast(intRandom(0, 255)) / 255.0F; + float blue = static_cast(intRandom(0, 255)) / 255.0F; + mat.diffuseColor = App::Color(red, green, blue); } else { - unsigned long shcol = hGrp->GetUnsigned("DefaultShapeColor", 3435980543UL); - r = ((shcol >> 24) & 0xff) / 255.0; - g = ((shcol >> 16) & 0xff) / 255.0; - b = ((shcol >> 8) & 0xff) / 255.0; + getColor("DefaultShapeColor", mat.diffuseColor); } - int initialTransparency = hGrp->GetInt("DefaultShapeTransparency", 0); + getColor("DefaultAmbientColor", mat.ambientColor); + getColor("DefaultEmissiveColor", mat.emissiveColor); + getColor("DefaultSpecularColor", mat.specularColor); + + long initialTransparency = hGrp->GetInt("DefaultShapeTransparency", 0); + long initialShininess = hGrp->GetInt("DefaultShapeShininess", 90); auto material = manager.getMaterial(defaultMaterialUUID()); if (material->hasAppearanceModel(ModelUUIDs::ModelUUID_Rendering_Basic)) { material->getAppearanceProperty(QString::fromLatin1("DiffuseColor")) - ->setColor(App::Color(r, g, b)); + ->setColor(mat.diffuseColor); + material->getAppearanceProperty(QString::fromLatin1("AmbientColor")) + ->setColor(mat.ambientColor); + material->getAppearanceProperty(QString::fromLatin1("EmissiveColor")) + ->setColor(mat.emissiveColor); + material->getAppearanceProperty(QString::fromLatin1("SpecularColor")) + ->setColor(mat.specularColor); material->getAppearanceProperty(QString::fromLatin1("Transparency")) - ->setFloat((float)initialTransparency / 100.0f); + ->setFloat((float)initialTransparency / 100.0F); + material->getAppearanceProperty(QString::fromLatin1("Shininess")) + ->setFloat((float)initialShininess / 100.0F); } return material; diff --git a/src/Mod/Material/App/PreCompiled.h b/src/Mod/Material/App/PreCompiled.h index e991f90cda..085b2d5ca4 100644 --- a/src/Mod/Material/App/PreCompiled.h +++ b/src/Mod/Material/App/PreCompiled.h @@ -50,6 +50,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Mod/Part/Gui/DlgSettingsObjectColor.cpp b/src/Mod/Part/Gui/DlgSettingsObjectColor.cpp index cf4ae774c3..f9ce22a357 100644 --- a/src/Mod/Part/Gui/DlgSettingsObjectColor.cpp +++ b/src/Mod/Part/Gui/DlgSettingsObjectColor.cpp @@ -51,8 +51,12 @@ void DlgSettingsObjectColor::saveSettings() { // Part ui->DefaultShapeColor->onSave(); + ui->DefaultAmbientColor->onSave(); + ui->DefaultEmissiveColor->onSave(); + ui->DefaultSpecularColor->onSave(); ui->checkRandomColor->onSave(); ui->DefaultShapeTransparency->onSave(); + ui->DefaultShapeShininess->onSave(); ui->DefaultShapeLineColor->onSave(); ui->DefaultShapeLineWidth->onSave(); ui->DefaultShapeVertexColor->onSave(); @@ -68,8 +72,12 @@ void DlgSettingsObjectColor::loadSettings() { // Part ui->DefaultShapeColor->onRestore(); + ui->DefaultAmbientColor->onRestore(); + ui->DefaultEmissiveColor->onRestore(); + ui->DefaultSpecularColor->onRestore(); ui->checkRandomColor->onRestore(); ui->DefaultShapeTransparency->onRestore(); + ui->DefaultShapeShininess->onRestore(); ui->DefaultShapeLineColor->onRestore(); ui->DefaultShapeLineWidth->onRestore(); ui->DefaultShapeVertexColor->onRestore(); diff --git a/src/Mod/Part/Gui/DlgSettingsObjectColor.ui b/src/Mod/Part/Gui/DlgSettingsObjectColor.ui index 2b24f20553..4df39794cc 100644 --- a/src/Mod/Part/Gui/DlgSettingsObjectColor.ui +++ b/src/Mod/Part/Gui/DlgSettingsObjectColor.ui @@ -72,6 +72,105 @@ + + + + 182 + 0 + + + + Ambient shape color + + + + + + + The default ambient color for new shapes + + + + 85 + 85 + 85 + + + + DefaultAmbientColor + + + View + + + + + + + + 182 + 0 + + + + Emissive shape color + + + + + + + The default emissive color for new shapes + + + + 0 + 0 + 0 + + + + DefaultEmissiveColor + + + View + + + + + + + + 182 + 0 + + + + Specular shape color + + + + + + + The default specular color for new shapes + + + + 136 + 136 + 136 + + + + DefaultSpecularColor + + + View + + + + @@ -84,7 +183,7 @@ - + The default transparency for new shapes @@ -106,7 +205,45 @@ - + + + + + 182 + 0 + + + + Shape shininess + + + + + + + The default shininess for new shapes + + + % + + + 100 + + + 90 + + + 5 + + + DefaultShapeShininess + + + View + + + + @@ -119,7 +256,7 @@ - + The default line color for new shapes @@ -139,7 +276,7 @@ - + @@ -152,7 +289,7 @@ - + The default line thickness for new shapes @@ -174,7 +311,7 @@ - + @@ -187,7 +324,7 @@ - + The default color for new vertices @@ -207,7 +344,7 @@ - + @@ -220,7 +357,7 @@ - + The default size for new vertices @@ -242,7 +379,7 @@ - + @@ -255,7 +392,7 @@ - + The color of bounding boxes in the 3D view @@ -275,7 +412,7 @@ - + @@ -288,7 +425,7 @@ - + The font size of bounding boxes in the 3D view @@ -316,7 +453,7 @@ - + diff --git a/tests/src/App/CMakeLists.txt b/tests/src/App/CMakeLists.txt index cdd545cadf..6718941efc 100644 --- a/tests/src/App/CMakeLists.txt +++ b/tests/src/App/CMakeLists.txt @@ -5,6 +5,7 @@ target_sources( PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/Application.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Branding.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Color.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ComplexGeoData.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Document.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Expression.cpp diff --git a/tests/src/App/Color.cpp b/tests/src/App/Color.cpp new file mode 100644 index 0000000000..99db0efd4c --- /dev/null +++ b/tests/src/App/Color.cpp @@ -0,0 +1,247 @@ +#include "gtest/gtest.h" +#include +#include "App/Color.h" + +// NOLINTBEGIN +TEST(TestColor, equal) +{ + int red = 85; + int green = 170; + int blue = 255; + + App::Color color(red / 255.0F, green / 255.0F, blue / 255.0F); + uint32_t packed = color.getPackedValue(); + + App::Color color2 {packed}; + EXPECT_EQ(color, color2); +} + +TEST(TestColor, round) +{ + for (int index = 0; index < 256; index++) { + float value = static_cast(index) / 255.0F; + EXPECT_EQ(std::lround(value * 255.0F), index); + } +} + +TEST(TestColor, packedValue) +{ + int red = 85; + int green = 170; + int blue = 255; + + App::Color color(red / 255.0F, green / 255.0F, blue / 255.0F); + uint32_t packed = color.getPackedValue(); + App::Color color2 {packed}; + + EXPECT_EQ(std::lround(color2.r * 255.0F), 85); + EXPECT_EQ(std::lround(color2.g * 255.0F), 170); + EXPECT_EQ(std::lround(color2.b * 255.0F), 255); +} + +TEST(TestColor, packedRGB) +{ + int red = 85; + int green = 170; + int blue = 255; + + App::Color color(red / 255.0F, green / 255.0F, blue / 255.0F); + uint32_t packed = color.getPackedRGB(); + App::Color color2; + color2.setPackedRGB(packed); + + EXPECT_EQ(std::lround(color2.r * 255.0F), 85); + EXPECT_EQ(std::lround(color2.g * 255.0F), 170); + EXPECT_EQ(std::lround(color2.b * 255.0F), 255); +} + +TEST(TestColor, packedARGB) +{ + int red = 85; + int green = 170; + int blue = 255; + int alpha = 127; + + App::Color color(red / 255.0F, green / 255.0F, blue / 255.0F, alpha / 255.0F); + uint32_t packed = color.getPackedARGB(); + App::Color color2; + color2.setPackedARGB(packed); + + EXPECT_EQ(std::lround(color2.r * 255.0F), 85); + EXPECT_EQ(std::lround(color2.g * 255.0F), 170); + EXPECT_EQ(std::lround(color2.b * 255.0F), 255); + EXPECT_EQ(std::lround(color2.a * 255.0F), 127); +} + +struct IntColor +{ + IntColor(int red, int green, int blue, int alpha = 0) + : red_ {red} + , green_ {green} + , blue_ {blue} + , alpha_ {alpha} + {} + + float redF() const + { + return static_cast(red_) / 255.0F; + } + + float greenF() const + { + return static_cast(green_) / 255.0F; + } + + float blueF() const + { + return static_cast(blue_) / 255.0F; + } + + float alphaF() const + { + return static_cast(alpha_) / 255.0F; + } + + int red() const + { + return red_; + } + + int green() const + { + return green_; + } + + int blue() const + { + return blue_; + } + + int alpha() const + { + return alpha_; + } + +private: + int red_ {}; + int green_ {}; + int blue_ {}; + int alpha_ {}; +}; + +TEST(TestColor, asPackedRGB) +{ + int red = 85; + int green = 170; + int blue = 255; + + IntColor intColor {red, green, blue}; + uint32_t packed = App::Color::asPackedRGB(intColor); + + + EXPECT_EQ(packed, 1437269760); +} + +TEST(TestColor, fromPackedRGB) +{ + int red = 85; + int green = 170; + int blue = 255; + + App::Color color(red / 255.0F, green / 255.0F, blue / 255.0F); + + IntColor intColor = App::Color::fromPackedRGB(color.getPackedRGB()); + + EXPECT_EQ(intColor.red(), red); + EXPECT_EQ(intColor.green(), green); + EXPECT_EQ(intColor.blue(), blue); +} + +TEST(TestColor, asPackedRGBA) +{ + int red = 85; + int green = 170; + int blue = 255; + int alpha = 127; + + IntColor intColor {red, green, blue, alpha}; + uint32_t packed = App::Color::asPackedRGBA(intColor); + + + EXPECT_EQ(packed, 1437269887); +} + +TEST(TestColor, fromPackedRGBA) +{ + int red = 85; + int green = 170; + int blue = 255; + int alpha = 127; + + App::Color color(red / 255.0F, green / 255.0F, blue / 255.0F, alpha / 255.0F); + + IntColor intColor = App::Color::fromPackedRGBA(color.getPackedValue()); + + EXPECT_EQ(intColor.red(), red); + EXPECT_EQ(intColor.green(), green); + EXPECT_EQ(intColor.blue(), blue); + EXPECT_EQ(intColor.alpha(), alpha); +} + +TEST(TestColor, setValue) +{ + int red = 85; + int green = 170; + int blue = 255; + + IntColor intColor {red, green, blue}; + App::Color color {}; + color.setValue(intColor); + + EXPECT_FLOAT_EQ(color.r, 85.0F / 255.0F); + EXPECT_FLOAT_EQ(color.g, 170.0F / 255.0F); + EXPECT_FLOAT_EQ(color.b, 255.0F / 255.0F); +} + +TEST(TestColor, asValue) +{ + int red = 85; + int green = 170; + int blue = 255; + + App::Color color(red / 255.0F, green / 255.0F, blue / 255.0F); + + IntColor intColor = color.asValue(); + + EXPECT_EQ(intColor.red(), 85); + EXPECT_EQ(intColor.green(), 170); + EXPECT_EQ(intColor.blue(), 255); +} + +TEST(TestColor, asHexString) +{ + int red = 85; + int green = 170; + int blue = 255; + + App::Color color(red / 255.0F, green / 255.0F, blue / 255.0F); + + EXPECT_EQ(color.asHexString(), "#55AAFF"); +} + +TEST(TestColor, fromHexString) +{ + App::Color color; + EXPECT_FALSE(color.fromHexString(std::string(""))); + EXPECT_FALSE(color.fromHexString(std::string("abcdef"))); + EXPECT_TRUE(color.fromHexString(std::string("#abcdef"))); + EXPECT_TRUE(color.fromHexString(std::string("#ABCDEF"))); + EXPECT_FALSE(color.fromHexString(std::string("#abcde"))); + EXPECT_FALSE(color.fromHexString(std::string("#abcdeff"))); + EXPECT_FALSE(color.fromHexString(std::string("#abcdeffff"))); + EXPECT_TRUE(color.fromHexString(std::string("#55AAFF"))); + EXPECT_FLOAT_EQ(color.r, 85.0F / 255.0F); + EXPECT_FLOAT_EQ(color.g, 170.0F / 255.0F); + EXPECT_FLOAT_EQ(color.b, 255.0F / 255.0F); +} +// NOLINTEND