From 2a28d8863cf0d271650ac144313e0b9c60c51347 Mon Sep 17 00:00:00 2001 From: Gawaboumga Date: Sun, 21 Feb 2016 14:42:38 +0100 Subject: [PATCH] Documentation for the rest Former-commit-id: b6f401370127679db397da0039cb5e98477e90db --- include/Nazara/Core/CallOnExit.inl | 27 +++ include/Nazara/Core/Color.inl | 217 +++++++++++++++++++++- include/Nazara/Core/ObjectLibrary.inl | 39 ++++ src/Nazara/Core/Clock.cpp | 2 +- src/Nazara/Core/GuillotineBinPack.cpp | 245 ++++++++++++++++++++++-- src/Nazara/Core/HardwareInfo.cpp | 108 +++++++++-- tests/Engine/Math/AlgorithmMath.cpp | 256 ++++++++++++++++++++++++++ 7 files changed, 851 insertions(+), 43 deletions(-) create mode 100644 tests/Engine/Math/AlgorithmMath.cpp diff --git a/include/Nazara/Core/CallOnExit.inl b/include/Nazara/Core/CallOnExit.inl index f89810a77..987da852e 100644 --- a/include/Nazara/Core/CallOnExit.inl +++ b/include/Nazara/Core/CallOnExit.inl @@ -7,17 +7,38 @@ namespace Nz { + /*! + * \class Nz::CallOnExit + * \brief Core class that represents a function to call at the end of the scope + */ + + /*! + * \brief Constructs a CallOnExit object with a function + * + * \param func Function to call on exit + */ + inline CallOnExit::CallOnExit(Func func) : m_func(func) { } + /*! + * \brief Destructs the object and calls the function + */ + inline CallOnExit::~CallOnExit() { if (m_func) m_func(); } + /*! + * \brief Calls the function and sets the new callback + * + * \param func Function to call on exit + */ + inline void CallOnExit::CallAndReset(Func func) { if (m_func) @@ -26,6 +47,12 @@ namespace Nz Reset(func); } + /*! + * \brief Resets the function + * + * \param func Function to call on exit + */ + inline void CallOnExit::Reset(Func func) { m_func = func; diff --git a/include/Nazara/Core/Color.inl b/include/Nazara/Core/Color.inl index ac576eee3..7dcfc7d75 100644 --- a/include/Nazara/Core/Color.inl +++ b/include/Nazara/Core/Color.inl @@ -11,10 +11,28 @@ namespace Nz { + /*! + * \class Nz::Color + * \brief Core class that represents a color + */ + + /*! + * \brief Constructs a Color object by default + */ + inline Color::Color() { } + /*! + * \brief Constructs a Color object with values + * + * \param red Red value + * \param green Green value + * \param blue Blue value + * \param alpha Alpha value + */ + inline Color::Color(UInt8 red, UInt8 green, UInt8 blue, UInt8 alpha) : r(red), g(green), @@ -23,6 +41,12 @@ namespace Nz { } + /*! + * \brief Constructs a Color object with a light level + * + * \param lightness Value for r, g and b + */ + inline Color::Color(UInt8 lightness) : r(lightness), g(lightness), @@ -31,6 +55,13 @@ namespace Nz { } + /*! + * \brief Constructs a Color object with values + * + * \param vec[3] vec[0] = red value, vec[1] = green value, vec[2] = blue value + * \param alpha Alpha value + */ + inline Color::Color(UInt8 vec[3], UInt8 alpha) : r(vec[0]), g(vec[1]), @@ -39,6 +70,11 @@ namespace Nz { } + /*! + * \brief Converts this to string + * \return String representation of the object "Color(r, g, b[, a])" + */ + inline String Color::ToString() const { StringStream ss; @@ -52,6 +88,13 @@ namespace Nz return ss; } + /*! + * \brief Adds two colors together + * \return Color which is the sum + * + * \param color Other color + */ + inline Color Color::operator+(const Color& color) const { ///TODO: Improve this shit @@ -64,6 +107,13 @@ namespace Nz return c; } + /*! + * \brief Multiplies two colors together + * \return Color which is the product + * + * \param color Other color + */ + inline Color Color::operator*(const Color& color) const { ///TODO: Improve this shit @@ -76,48 +126,104 @@ namespace Nz return c; } + /*! + * \brief Adds the color to this + * \return Color which is the sum + * + * \param color Other color + */ + inline Color Color::operator+=(const Color& color) { return operator=(operator+(color)); } + /*! + * \brief Multiplies the color to this + * \return Color which is the product + * + * \param color Other color + */ + inline Color Color::operator*=(const Color& color) { return operator=(operator*(color)); } + /*! + * \brief Checks whether the two colors are equal + * \return true if it is the case + * + * \param color Color to compare + */ + inline bool Color::operator==(const Color& color) const { return r == color.r && g == color.g && b == color.b && a == color.a; } + /*! + * \brief Checks whether the two colors are equal + * \return false if it is the case + * + * \param color Color to compare + */ + inline bool Color::operator!=(const Color& color) const { return !operator==(color); } - // Algorithmes venant de http://www.easyrgb.com/index.php?X=MATH + // Algorithm coming from http://www.easyrgb.com/index.php?X=MATH + + /*! + * \brief Converts CMY representation to RGB + * \return Color resulting + * + * \param cyan Cyan component + * \param magenta Magenta component + * \param yellow Yellow component + */ inline Color Color::FromCMY(float cyan, float magenta, float yellow) { return Color(static_cast((1.f-cyan)*255.f), static_cast((1.f-magenta)*255.f), static_cast((1.f-yellow)*255.f)); } + /*! + * \brief Converts CMYK representation to RGB + * \return Color resulting + * + * \param cyan Cyan component + * \param magenta Magenta component + * \param yellow Yellow component + * \param black Black component + */ + inline Color Color::FromCMYK(float cyan, float magenta, float yellow, float black) { return FromCMY(cyan * (1.f - black) + black, - magenta * (1.f - black) + black, - yellow * (1.f - black) + black); + magenta * (1.f - black) + black, + yellow * (1.f - black) + black); } + /*! + * \brief Converts HSL representation to RGB + * \return Color resulting + * + * \param hue Hue component + * \param saturation Saturation component + * \param lightness Lightness component + */ + inline Color Color::FromHSL(UInt8 hue, UInt8 saturation, UInt8 lightness) { if (saturation == 0) { // RGB results from 0 to 255 return Color(lightness * 255, - lightness * 255, - lightness * 255); + lightness * 255, + lightness * 255); } else { @@ -135,11 +241,20 @@ namespace Nz float v1 = 2.f * l - v2; return Color(static_cast(255.f * Hue2RGB(v1, v2, h + (1.f/3.f))), - static_cast(255.f * Hue2RGB(v1, v2, h)), - static_cast(255.f * Hue2RGB(v1, v2, h - (1.f/3.f)))); + static_cast(255.f * Hue2RGB(v1, v2, h)), + static_cast(255.f * Hue2RGB(v1, v2, h - (1.f/3.f)))); } } + /*! + * \brief Converts HSV representation to RGB + * \return Color resulting + * + * \param hue Hue component + * \param saturation Saturation component + * \param value Value component + */ + inline Color Color::FromHSV(float hue, float saturation, float value) { if (NumberEquals(saturation, 0.f)) @@ -201,11 +316,28 @@ namespace Nz return Color(static_cast(r*255.f), static_cast(g*255.f), static_cast(b*255.f)); } } + + /*! + * \brief Converts XYZ representation to RGB + * \return Color resulting + * + * \param vec Vector3 representing the space color + */ + inline Color Color::FromXYZ(const Vector3f& vec) { return FromXYZ(vec.x, vec.y, vec.z); } + /*! + * \brief Converts XYZ representation to RGB + * \return Color resulting + * + * \param x X component + * \param y Y component + * \param z Z component + */ + inline Color Color::FromXYZ(float x, float y, float z) { x /= 100.f; // X from 0 to 95.047 @@ -234,6 +366,15 @@ namespace Nz return Color(static_cast(r * 255.f), static_cast(g * 255.f), static_cast(b * 255.f)); } + /*! + * \brief Converts RGB representation to CMYK + * + * \param color Color to transform + * \param cyan Cyan component + * \param magenta Magenta component + * \param yellow Yellow component + */ + inline void Color::ToCMY(const Color& color, float* cyan, float* magenta, float* yellow) { *cyan = 1.f - color.r/255.f; @@ -241,6 +382,15 @@ namespace Nz *yellow = 1.f - color.b/255.f; } + /*! + * \brief Converts RGB representation to CMYK + * + * \param color Color to transform + * \param cyan Cyan component + * \param magenta Magenta component + * \param yellow Yellow component + */ + inline void Color::ToCMYK(const Color& color, float* cyan, float* magenta, float* yellow, float* black) { float c, m, y; @@ -265,6 +415,15 @@ namespace Nz *black = k; } + /*! + * \brief Converts RGB representation to HSL + * + * \param color Color to transform + * \param hue Hue component + * \param saturation Saturation component + * \param lightness Lightness component + */ + inline void Color::ToHSL(const Color& color, UInt8* hue, UInt8* saturation, UInt8* lightness) { float r = color.r / 255.f; @@ -315,6 +474,15 @@ namespace Nz } } + /*! + * \brief Converts RGB representation to HSV + * + * \param color Color to transform + * \param hue Hue component + * \param saturation Saturation component + * \param value Value component + */ + inline void Color::ToHSV(const Color& color, float* hue, float* saturation, float* value) { float r = color.r / 255.f; @@ -361,11 +529,27 @@ namespace Nz } } + /*! + * \brief Converts RGB representation to XYZ + * + * \param color Color to transform + * \param vec Vector3 representing the space color + */ + inline void Color::ToXYZ(const Color& color, Vector3f* vec) { return ToXYZ(color, &vec->x, &vec->y, &vec->z); } + /*! + * \brief Converts RGB representation to XYZ + * + * \param color Color to transform + * \param x X component + * \param y Y component + * \param z Z component + */ + inline void Color::ToXYZ(const Color& color, float* x, float* y, float* z) { float r = color.r/255.f; //R from 0 to 255 @@ -397,6 +581,15 @@ namespace Nz *z = r*0.0193f + g*0.1192f + b*0.9505f; } + /*! + * \brief Converts HUE representation to RGV + * \return RGB corresponding + * + * \param v1 V1 component + * \param v2 V2 component + * \param vH VH component + */ + inline float Color::Hue2RGB(float v1, float v2, float vH) { if (vH < 0.f) @@ -415,8 +608,16 @@ namespace Nz return v1 + (v2 - v1)*(2.f/3.f - vH)*6; return v1; + } } -} + +/*! +* \brief Output operator +* \return The stream +* +* \param out The stream +* \param color The color to output +*/ inline std::ostream& operator<<(std::ostream& out, const Nz::Color& color) { diff --git a/include/Nazara/Core/ObjectLibrary.inl b/include/Nazara/Core/ObjectLibrary.inl index 998adece4..41497a808 100644 --- a/include/Nazara/Core/ObjectLibrary.inl +++ b/include/Nazara/Core/ObjectLibrary.inl @@ -7,6 +7,20 @@ namespace Nz { + /*! + * \class Nz::ObjectRef + * \brief Core class that represents a reference to an object + */ + + /*! + * \brief Gets the ObjectRef object by name + * \return Optional reference + * + * \param name Name of the object + * + * \remark Produces a NazaraError if object not found + */ + template ObjectRef ObjectLibrary::Get(const String& name) { @@ -17,18 +31,37 @@ namespace Nz return ref; } + /*! + * \brief Checks whether the library has the object with that name + * \return true if it the case + */ + template bool ObjectLibrary::Has(const String& name) { return Type::s_library.find(name) != Type::s_library.end(); } + /*! + * \brief Registers the ObjectRef object with that name + * + * \param name Name of the object + * \param object Object to stock + */ + template void ObjectLibrary::Register(const String& name, ObjectRef object) { Type::s_library.emplace(name, object); } + /*! + * \brief Gets the ObjectRef object by name + * \return Optional reference + * + * \param name Name of the object + */ + template ObjectRef ObjectLibrary::Query(const String& name) { @@ -39,6 +72,12 @@ namespace Nz return nullptr; } + /*! + * \brief Unregisters the ObjectRef object with that name + * + * \param name Name of the object + */ + template void ObjectLibrary::Unregister(const String& name) { diff --git a/src/Nazara/Core/Clock.cpp b/src/Nazara/Core/Clock.cpp index dae4c84d5..ef4689d50 100644 --- a/src/Nazara/Core/Clock.cpp +++ b/src/Nazara/Core/Clock.cpp @@ -46,7 +46,7 @@ namespace Nz * \brief Utility class that measure the elapsed time */ - /*! + /*! * \brief Constructs a Clock object * * \param startingValue The starting time value, in microseconds diff --git a/src/Nazara/Core/GuillotineBinPack.cpp b/src/Nazara/Core/GuillotineBinPack.cpp index 05f8736b5..69e3a1333 100644 --- a/src/Nazara/Core/GuillotineBinPack.cpp +++ b/src/Nazara/Core/GuillotineBinPack.cpp @@ -16,13 +16,36 @@ namespace Nz { + /*! + * \class Nz::GuillotineBinPack + * \brief Core class that represents the "Guillotine problem", combination of the "Bin packing problem" and the "cutting stock" + */ + namespace { + /*! + * \brief Gets the score for fitting the area + * \return Score of the fitting + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreBestAreaFit(int width, int height, const Rectui& freeRectSize) { return freeRectSize.width * freeRectSize.height - width * height; } + /*! + * \brief Gets the score for fitting the area following long side + * \return Score of the fitting following long side + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreBestLongSideFit(int width, int height, const Rectui& freeRectSize) { int leftoverHoriz = std::abs(static_cast(freeRectSize.width - width)); @@ -32,6 +55,15 @@ namespace Nz return leftover; } + /*! + * \brief Gets the score for fitting the area following short side + * \return Score of the fitting following short side + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreBestShortSideFit(int width, int height, const Rectui& freeRectSize) { int leftoverHoriz = std::abs(static_cast(freeRectSize.width - width)); @@ -41,37 +73,85 @@ namespace Nz return leftover; } + /*! + * \brief Gets the worst score for fitting the area + * \return Worst score of the fitting + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreWorstAreaFit(int width, int height, const Rectui& freeRectSize) { return -ScoreBestAreaFit(width, height, freeRectSize); } + /*! + * \brief Gets the worst score for fitting the area following long side + * \return Worst score of the fitting following long side + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreWorstLongSideFit(int width, int height, const Rectui& freeRectSize) { return -ScoreBestLongSideFit(width, height, freeRectSize); } + /*! + * \brief Gets the worst score for fitting the area following short side + * \return Worst score of the fitting following short side + * + * \param width Width + * \param height Height + * \param freeRectSize Free area + */ + int ScoreWorstShortSideFit(int width, int height, const Rectui& freeRectSize) { return -ScoreBestShortSideFit(width, height, freeRectSize); } } + /*! + * \brief Constructs a GuillotineBinPack object by default + */ + GuillotineBinPack::GuillotineBinPack() { Reset(); } + /*! + * \brief Constructs a GuillotineBinPack object with width and height + * + * \param width Width + * \param height Height + */ + GuillotineBinPack::GuillotineBinPack(unsigned int width, unsigned int height) { Reset(width, height); } + /*! + * \brief Constructs a GuillotineBinPack object with area + * + * \param size Vector2 representing the area (width, height) + */ + GuillotineBinPack::GuillotineBinPack(const Vector2ui& size) { Reset(size); } + /*! + * \brief Clears the content + */ + void GuillotineBinPack::Clear() { m_freeRectangles.clear(); @@ -80,6 +160,15 @@ namespace Nz m_usedArea = 0; } + /*! + * \brief Expands the content + * + * \param newWidth New width for the expansion + * \param newHeight New height for the expansion + * + * \see Expand + */ + void GuillotineBinPack::Expand(unsigned int newWidth, unsigned newHeight) { unsigned int oldWidth = m_width; @@ -98,52 +187,123 @@ namespace Nz while (MergeFreeRectangles()); } + /*! + * \brief Expands the content + * + * \param newSize New area for the expansion + * + * \see Expand + */ + void GuillotineBinPack::Expand(const Vector2ui& newSize) { Expand(newSize.x, newSize.y); } + /*! + * \brief Frees the rectangle + * + * \param rect Area to free + * + * \remark This method should only be called with computed rectangles by the method Insert and can produce fragmentation + */ + void GuillotineBinPack::FreeRectangle(const Rectui& rect) { - ///DOC: Cette méthode ne devrait recevoir que des rectangles calculés par la méthode Insert et peut provoquer de la fragmentation m_freeRectangles.push_back(rect); m_usedArea -= rect.width * rect.height; } + /*! + * \brief Gets the height + * \return Height of the area + */ + unsigned int GuillotineBinPack::GetHeight() const { return m_height; } + /*! + * \brief Gets percentage of occupation + * \return Percentage of the already occupied area + */ + float GuillotineBinPack::GetOccupancy() const { return static_cast(m_usedArea)/(m_width*m_height); } + /*! + * \brief Gets the size of the area + * \return Size of the area + */ + Vector2ui GuillotineBinPack::GetSize() const { return Vector2ui(m_width, m_height); } + /*! + * \brief Gets the width + * \return Width of the area + */ + unsigned int GuillotineBinPack::GetWidth() const { return m_width; } + /*! + * \brief Inserts rectangles in the area + * \return true if each rectangle could be inserted + * + * \param rects List of rectangles + * \param count Count of rectangles + * \param merge Merge possible + * \param rectChoice Heuristic to use to free + * \param splitMethod Heuristic to use to split + */ + bool GuillotineBinPack::Insert(Rectui* rects, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) { return Insert(rects, nullptr, nullptr, count, merge, rectChoice, splitMethod); } + /*! + * \brief Inserts rectangles in the area + * \return true if each rectangle could be inserted + * + * \param rects List of rectangles + * \param flipped List of flipped rectangles + * \param count Count of rectangles + * \param merge Merge possible + * \param rectChoice Heuristic to use to free + * \param splitMethod Heuristic to use to split + */ + bool GuillotineBinPack::Insert(Rectui* rects, bool* flipped, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) { return Insert(rects, flipped, nullptr, count, merge, rectChoice, splitMethod); } + /*! + * \brief Inserts rectangles in the area + * \return true if each rectangle could be inserted + * + * \param rects List of rectangles + * \param flipped List of flipped rectangles + * \param flipped List of inserted rectangles + * \param count Count of rectangles + * \param merge Merge possible + * \param rectChoice Heuristic to use to free + * \param splitMethod Heuristic to use to split + */ + bool GuillotineBinPack::Insert(Rectui* rects, bool* flipped, bool* inserted, unsigned int count, bool merge, FreeRectChoiceHeuristic rectChoice, GuillotineSplitHeuristic splitMethod) { - std::vector remainingRects(count); // La position du rectangle + std::vector remainingRects(count); // Position of the rectangle for (unsigned int i = 0; i < count; ++i) remainingRects[i] = &rects[i]; @@ -214,7 +374,7 @@ namespace Nz // If we didn't manage to find any rectangle to pack, abort. if (bestScore == std::numeric_limits::max()) { - // Si nous le pouvons, on marque les rectangles n'ayant pas pu être insérés + // If we can do it, we mark the rectangle could be inserted if (inserted) { for (Rectui* rect : remainingRects) @@ -259,9 +419,13 @@ namespace Nz return true; } + /*! + * \brief Merges free rectangles together + * \return true if there was a merge (and thus if a merge is still possible) + */ + bool GuillotineBinPack::MergeFreeRectangles() { - ///DOC: Renvoie true s'il y a eu fusion (et donc si une fusion est encore possible) std::size_t oriSize = m_freeRectangles.size(); // Do a Theta(n^2) loop to see if any pair of free rectangles could me merged into one. @@ -312,6 +476,10 @@ namespace Nz return m_freeRectangles.size() < oriSize; } + /*! + * \brief Resets the area + */ + void GuillotineBinPack::Reset() { m_height = 0; @@ -320,6 +488,13 @@ namespace Nz Clear(); } + /*! + * \brief Resets the area + * + * \param width Width + * \param height Height + */ + void GuillotineBinPack::Reset(unsigned int width, unsigned int height) { m_height = height; @@ -328,11 +503,25 @@ namespace Nz Clear(); } + /*! + * \brief Resets the area + * + * \param size Size of the area + */ + void GuillotineBinPack::Reset(const Vector2ui& size) { Reset(size.x, size.y); } + /*! + * \brief Splits the free rectangle along axis + * + * \param freeRect Free rectangle to split + * \param placedRect Already placed rectangle + * \param splitHorizontal Split horizontally (or vertically) + */ + void GuillotineBinPack::SplitFreeRectAlongAxis(const Rectui& freeRect, const Rectui& placedRect, bool splitHorizontal) { // Form the two new rectangles. @@ -365,50 +554,60 @@ namespace Nz m_freeRectangles.push_back(right); } + /*! + * \brief Splits the free rectangle using the heuristic + * + * \param freeRect Free rectangle to split + * \param placedRect Already placed rectangle + * \param method Method used to split + * + * \remark Produces a NazaraError if enumeration GuillotineSplitHeuristic is invalid + */ + void GuillotineBinPack::SplitFreeRectByHeuristic(const Rectui& freeRect, const Rectui& placedRect, GuillotineSplitHeuristic method) { - // Compute the lengths of the leftover area. + // Compute the lengths of the leftover area const int w = freeRect.width - placedRect.width; const int h = freeRect.height - placedRect.height; // Placing placedRect into freeRect results in an L-shaped free area, which must be split into - // two disjoint rectangles. This can be achieved with by splitting the L-shape using a single line. - // We have two choices: horizontal or vertical. + // two disjoint rectangles. This can be achieved with by splitting the L-shape using a single line + // We have two choices: horizontal or vertical - // Use the given heuristic to decide which choice to make. + // Use the given heuristic to decide which choice to make bool splitHorizontal; switch (method) { case SplitLongerAxis: - // Split along the longer total axis. + // Split along the longer total axis splitHorizontal = (freeRect.width > freeRect.height); break; case SplitLongerLeftoverAxis: - // Split along the longer leftover axis. + // Split along the longer leftover axis splitHorizontal = (w > h); break; case SplitMaximizeArea: - // Maximize the smaller area == minimize the larger area. - // Tries to make the rectangles more even-sized. + // Maximize the smaller area == minimize the larger area + // Tries to make the rectangles more even-sized splitHorizontal = (placedRect.width * h <= w * placedRect.height); break; case SplitMinimizeArea: - // Maximize the larger area == minimize the smaller area. - // Tries to make the single bigger rectangle. + // Maximize the larger area == minimize the smaller area + // Tries to make the single bigger rectangle splitHorizontal = (placedRect.width * h > w * placedRect.height); break; case SplitShorterAxis: - // Split along the shorter total axis. + // Split along the shorter total axis splitHorizontal = (freeRect.width <= freeRect.height); break; case SplitShorterLeftoverAxis: - // Split along the shorter leftover axis. + // Split along the shorter leftover axis splitHorizontal = (w <= h); break; @@ -417,10 +616,22 @@ namespace Nz splitHorizontal = true; } - // Perform the actual split. + // Perform the actual split SplitFreeRectAlongAxis(freeRect, placedRect, splitHorizontal); } + /*! + * \brief Gets the score using heuristic + * \return Score of the heuristic + * + * \param width Width + * \param height Height + * \param freeRect Free area + * \param rectChoice Heuristic to get score + * + * \remark Produces a NazaraError if enumeration FreeRectChoiceHeuristic is invalid + */ + int GuillotineBinPack::ScoreByHeuristic(int width, int height, const Rectui& freeRect, FreeRectChoiceHeuristic rectChoice) { switch (rectChoice) diff --git a/src/Nazara/Core/HardwareInfo.cpp b/src/Nazara/Core/HardwareInfo.cpp index 1ada7a794..af7f47e5d 100644 --- a/src/Nazara/Core/HardwareInfo.cpp +++ b/src/Nazara/Core/HardwareInfo.cpp @@ -82,11 +82,31 @@ namespace Nz char s_brandString[48] = "Not initialized"; } + /*! + * \class Nz::HardwareInfo + * \brief Core class that represents the info we can get from hardware + */ + + /*! + * \brief Generates the cpuid instruction (available on x86 & x64) + * + * \param functionId Information to retrieve + * \param subFunctionId Additional code for information retrieval + * \param result Supported features of the CPU + */ + void HardwareInfo::Cpuid(UInt32 functionId, UInt32 subFunctionId, UInt32 result[4]) { return HardwareInfoImpl::Cpuid(functionId, subFunctionId, result); } + /*! + * \brief Gets the brand of the processor + * \return String of the brand + * + * \remark Produces a NazaraError if not Initialize + */ + String HardwareInfo::GetProcessorBrandString() { if (!Initialize()) @@ -95,13 +115,26 @@ namespace Nz return s_brandString; } + /*! + * \brief Gets the number of threads + * \return Number of threads available on the CPU + * + * \remark Doesn't need the initialization of HardwareInfo + */ + unsigned int HardwareInfo::GetProcessorCount() { - ///DOC: Ne nécessite pas l'initialisation de HardwareInfo pour fonctionner static unsigned int processorCount = std::max(HardwareInfoImpl::GetProcessorCount(), 1U); return processorCount; } + /*! + * \brief Gets the processor vendor + * \return ProcessorVendor containing information the vendor + * + * \remark Produces a NazaraError if not Initialize + */ + ProcessorVendor HardwareInfo::GetProcessorVendor() { if (!Initialize()) @@ -110,6 +143,13 @@ namespace Nz return s_vendorEnum; } + /*! + * \brief Gets the vendor of the processor + * \return String of the vendor + * + * \remark Produces a NazaraError if not Initialize + */ + String HardwareInfo::GetProcessorVendorName() { if (!Initialize()) @@ -118,13 +158,26 @@ namespace Nz return vendorNames[s_vendorEnum+1]; } + /*! + * \brief Gets the amount of total memory + * \return Number of total memory available + * + * \remark Doesn't need the initialization of HardwareInfo + */ + UInt64 HardwareInfo::GetTotalMemory() { - ///DOC: Ne nécessite pas l'initialisation de HardwareInfo pour fonctionner static UInt64 totalMemory = HardwareInfoImpl::GetTotalMemory(); return totalMemory; } + /*! + * \brief Checks whether the processor owns the capacity to handle certain instructions + * \return true If instructions supported + * + * \remark Produces a NazaraError if capability is a wrong enum with NAZARA_DEBUG defined + */ + bool HardwareInfo::HasCapability(ProcessorCap capability) { #ifdef NAZARA_DEBUG @@ -138,9 +191,16 @@ namespace Nz return s_capabilities[capability]; } + /*! + * \brief Initializes the HardwareInfo class + * \return true if successful + * + * \remark Produces a NazaraError if cpuid is not supported + */ + bool HardwareInfo::Initialize() { - if (s_initialized) + if (IsInitialized()) return true; if (!HardwareInfoImpl::IsCpuidSupported()) @@ -151,21 +211,21 @@ namespace Nz s_initialized = true; - UInt32 registers[4]; // Récupère les quatre registres (EAX, EBX, ECX et EDX) + UInt32 registers[4]; // Get the four registers (EAX, EBX, ECX et EDX) - // Pour plus de clarté + // To make it more clear UInt32& eax = registers[0]; UInt32& ebx = registers[1]; UInt32& ecx = registers[2]; UInt32& edx = registers[3]; - // Pour commencer, on va récupérer l'identifiant du constructeur ainsi que l'id de fonction maximal supporté par le CPUID + // To begin, we get the id of the constructor and the id of maximal functions supported by the CPUID HardwareInfoImpl::Cpuid(0, 0, registers); - // Attention à l'ordre : EBX, EDX, ECX + // Watchout to the order : EBX, EDX, ECX UInt32 manufacturerId[3] = {ebx, edx, ecx}; - // Identification du concepteur + // Identification of conceptor s_vendorEnum = ProcessorVendor_Unknown; for (const VendorString& vendorString : vendorStrings) { @@ -178,7 +238,7 @@ namespace Nz if (eax >= 1) { - // Récupération de certaines capacités du processeur (ECX et EDX, fonction 1) + // Recuperation of certain capacities of the processor (ECX et EDX, function 1) HardwareInfoImpl::Cpuid(1, 0, registers); s_capabilities[ProcessorCap_AVX] = (ecx & (1U << 28)) != 0; @@ -192,53 +252,67 @@ namespace Nz s_capabilities[ProcessorCap_SSE42] = (ecx & (1U << 20)) != 0; } - // Récupération de la plus grande fonction étendue supportée (EAX, fonction 0x80000000) + // Recuperation of biggest extended function handled (EAX, fonction 0x80000000) HardwareInfoImpl::Cpuid(0x80000000, 0, registers); UInt32 maxSupportedExtendedFunction = eax; if (maxSupportedExtendedFunction >= 0x80000001) { - // Récupération des capacités étendues du processeur (ECX et EDX, fonction 0x80000001) + // Recuperation of extended capabilities of the processor (ECX et EDX, fonction 0x80000001) HardwareInfoImpl::Cpuid(0x80000001, 0, registers); - s_capabilities[ProcessorCap_x64] = (edx & (1U << 29)) != 0; // Support du 64bits, indépendant de l'OS + s_capabilities[ProcessorCap_x64] = (edx & (1U << 29)) != 0; // Support of 64bits, independant of the OS s_capabilities[ProcessorCap_FMA4] = (ecx & (1U << 16)) != 0; s_capabilities[ProcessorCap_SSE4a] = (ecx & (1U << 6)) != 0; s_capabilities[ProcessorCap_XOP] = (ecx & (1U << 11)) != 0; if (maxSupportedExtendedFunction >= 0x80000004) { - // Récupération d'une chaîne de caractère décrivant le processeur (EAX, EBX, ECX et EDX, - // fonctions de 0x80000002 à 0x80000004 compris) + // Recuperation of the string describing the processor (EAX, EBX, ECX et EDX, + // functions from 0x80000002 to 0x80000004 inclusive) char* ptr = &s_brandString[0]; for (UInt32 code = 0x80000002; code <= 0x80000004; ++code) { HardwareInfoImpl::Cpuid(code, 0, registers); - std::memcpy(ptr, ®isters[0], 4*sizeof(UInt32)); // On rajoute les 16 octets à la chaîne + std::memcpy(ptr, ®isters[0], 4*sizeof(UInt32)); // We add the 16 bytes to the string ptr += 4*sizeof(UInt32); } - // Le caractère nul faisant partie de la chaîne retournée par le CPUID, pas besoin de le rajouter + // The character '\0' is already returned } } return true; } + /*! + * \brief Checks whether the instruction of cpuid is supported + * \return true if it the case + */ + bool HardwareInfo::IsCpuidSupported() { return HardwareInfoImpl::IsCpuidSupported(); } + /*! + * \brief Checks whether the class HardwareInfo is initialized + * \return true if it is initialized + */ + bool HardwareInfo::IsInitialized() { return s_initialized; } + /*! + * \brief Unitializes the class HardwareInfo + */ + void HardwareInfo::Uninitialize() { - // Rien à faire + // Nothing to do s_initialized = false; } } diff --git a/tests/Engine/Math/AlgorithmMath.cpp b/tests/Engine/Math/AlgorithmMath.cpp new file mode 100644 index 000000000..01413d902 --- /dev/null +++ b/tests/Engine/Math/AlgorithmMath.cpp @@ -0,0 +1,256 @@ +#include +#include + +TEST_CASE("Approach", "[MATH][ALGORITHM]") +{ + SECTION("Approach 8 with 5 by 2") + { + REQUIRE(Nz::Approach(5, 8, 2) == 7); + } + + SECTION("Approach 5 with 8 by 2") + { + REQUIRE(Nz::Approach(8, 5, 2) == 6); + } + + SECTION("Approach 8 with 8 by 2") + { + REQUIRE(Nz::Approach(8, 8, 2) == 8); + } +} + +TEST_CASE("Clamp", "[MATH][ALGORITHM]") +{ + SECTION("Clamp 8 between 5 and 10") + { + REQUIRE(Nz::Clamp(8, 5, 10) == 8); + } + + SECTION("Clamp 4 between 5 and 10") + { + REQUIRE(Nz::Clamp(4, 5, 10) == 5); + } + + SECTION("Clamp 12 between 5 and 10") + { + REQUIRE(Nz::Clamp(12, 5, 10) == 10); + } +} + +TEST_CASE("CountBits", "[MATH][ALGORITHM]") +{ + SECTION("Number 10 has 2 bits set to 1") + { + REQUIRE(Nz::CountBits(10) == 2); + } + + SECTION("Number 0 has 0 bit set to 1") + { + REQUIRE(Nz::CountBits(0) == 0); + } +} + +TEST_CASE("DegreeToRadian", "[MATH][ALGORITHM]") +{ + SECTION("Convert 45.f degree to radian") + { + REQUIRE(Nz::DegreeToRadian(45.f) == Approx(M_PI / 4)); + } +} + +TEST_CASE("GetNearestPowerOfTwo", "[MATH][ALGORITHM]") +{ + SECTION("Nearest power of two of 0 = 1") + { + REQUIRE(Nz::GetNearestPowerOfTwo(0) == 1); + } + + SECTION("Nearest power of two of 16 = 16") + { + REQUIRE(Nz::GetNearestPowerOfTwo(16) == 16); + } + + SECTION("Nearest power of two of 17 = 32") + { + REQUIRE(Nz::GetNearestPowerOfTwo(17) == 32); + } +} + +TEST_CASE("GetNumberLength", "[MATH][ALGORITHM]") +{ + SECTION("GetNumberLength of -127 signed char") + { + signed char minus127 = -127; + REQUIRE(Nz::GetNumberLength(minus127) == 4); + } + + SECTION("GetNumberLength of 255 unsigned char") + { + unsigned char plus255 = 255; + REQUIRE(Nz::GetNumberLength(plus255) == 3); + } + + SECTION("GetNumberLength of -1270 signed int") + { + signed int minus1270 = -1270; + REQUIRE(Nz::GetNumberLength(minus1270) == 5); + } + + SECTION("GetNumberLength of 2550 unsigned int") + { + unsigned int plus2550 = 2550; + REQUIRE(Nz::GetNumberLength(plus2550) == 4); + } + + SECTION("GetNumberLength of -1270 signed long long") + { + signed long long minus12700 = -12700; + REQUIRE(Nz::GetNumberLength(minus12700) == 6); + } + + SECTION("GetNumberLength of 2550 unsigned long long") + { + unsigned long long plus25500 = 25500; + REQUIRE(Nz::GetNumberLength(plus25500) == 5); + } + + SECTION("GetNumberLength of -2.456f float") + { + float minus2P456 = -2.456f; + REQUIRE(Nz::GetNumberLength(minus2P456, 3) == 6); + } + + SECTION("GetNumberLength of -2.456 double") + { + double minus2P456 = -2.456; + REQUIRE(Nz::GetNumberLength(minus2P456, 3) == 6); + } + + SECTION("GetNumberLength of -2.456 long double") + { + long double minus2P456 = -2.456L; + REQUIRE(Nz::GetNumberLength(minus2P456, 3) == 6); + } +} + +TEST_CASE("IntegralLog2", "[MATH][ALGORITHM]") +{ + SECTION("According to implementation, log in base 2 of 0 = 0") + { + REQUIRE(Nz::IntegralLog2(0) == 0); + } + + SECTION("Log in base 2 of 1 = 0") + { + REQUIRE(Nz::IntegralLog2(1) == 0); + } + + SECTION("Log in base 2 of 4 = 2") + { + REQUIRE(Nz::IntegralLog2(4) == 2); + } + + SECTION("Log in base 2 of 5 = 2") + { + REQUIRE(Nz::IntegralLog2(5) == 2); + } +} + +TEST_CASE("IntegralLog2Pot", "[MATH][ALGORITHM]") +{ + SECTION("According to implementation, log in base 2 of 0 = 0") + { + REQUIRE(Nz::IntegralLog2Pot(0) == 0); + } + + SECTION("Log in base 2 of 1 = 0") + { + REQUIRE(Nz::IntegralLog2Pot(1) == 0); + } + + SECTION("Log in base 2 of 4 = 2") + { + REQUIRE(Nz::IntegralLog2Pot(4) == 2); + } +} + +TEST_CASE("IntegralPow", "[MATH][ALGORITHM]") +{ + SECTION("2 to power 4") + { + REQUIRE(Nz::IntegralPow(2, 4) == 16); + } +} + +TEST_CASE("Lerp", "[MATH][ALGORITHM]") +{ + SECTION("Lerp 2 to 6 with 0.5") + { + REQUIRE(Nz::Lerp(2, 6, 0.5) == 4); + } +} + +TEST_CASE("MultiplyAdd", "[MATH][ALGORITHM]") +{ + SECTION("2 * 3 + 1") + { + REQUIRE(Nz::MultiplyAdd(2, 3, 1) == 7); + } +} + +TEST_CASE("NumberEquals", "[MATH][ALGORITHM]") +{ + SECTION("2.35 and 2.351 should be the same at 0.01") + { + CHECK(Nz::NumberEquals(2.35, 2.35, 0.01)); + } + + SECTION("3 and 4 unsigned should be the same at 1") + { + CHECK(Nz::NumberEquals(3U, 4U, 1U)); + } +} + +TEST_CASE("NumberToString", "[MATH][ALGORITHM]") +{ + SECTION("235 to string") + { + REQUIRE(Nz::NumberToString(235) == "235"); + } + + SECTION("-235 to string") + { + REQUIRE(Nz::NumberToString(-235) == "-235"); + } + + SECTION("16 in base 16 to string") + { + REQUIRE(Nz::NumberToString(16, 16) == "10"); + } +} + +TEST_CASE("RadianToDegree", "[MATH][ALGORITHM]") +{ + SECTION("PI / 4 to degree") + { + REQUIRE(Nz::RadianToDegree(M_PI / 4) == Approx(45.f)); + } +} + +TEST_CASE("StringToNumber", "[MATH][ALGORITHM]") +{ + SECTION("235 in string") + { + REQUIRE(Nz::StringToNumber("235") == 235); + } + + SECTION("-235 in string") + { + REQUIRE(Nz::StringToNumber("-235") == -235); + } + + SECTION("16 in base 16 in string") + { + REQUIRE(Nz::StringToNumber("10", 16) == 16); + } +}