diff options
Diffstat (limited to 'source/blender/blenlib')
33 files changed, 1470 insertions, 409 deletions
diff --git a/source/blender/blenlib/BLI_color.hh b/source/blender/blenlib/BLI_color.hh index e57a5109a66..287587e04be 100644 --- a/source/blender/blenlib/BLI_color.hh +++ b/source/blender/blenlib/BLI_color.hh @@ -22,41 +22,122 @@ namespace blender { -struct Color4f { - float r, g, b, a; +/** + * CPP based color structures. + * + * Strongly typed color storage structures with space and alpha association. + * Will increase readability and visibility of typical mistakes when + * working with colors. + * + * The storage structs can hold 4 channels (r, g, b and a). + * + * Usage: + * + * Convert a theme byte color to a linearrgb premultiplied. + * ``` + * ColorTheme4b theme_color; + * ColorSceneLinear4f<eAlpha::Premultiplied> linearrgb_color = + * BLI_color_convert_to_scene_linear(theme_color).premultiply_alpha(); + * ``` + * + * The API is structured to make most use of inlining. Most notable are space + * conversions done via `BLI_color_convert_to*` functions. + * + * - Conversions between spaces (theme <=> scene linear) should always be done by + * invoking the `BLI_color_convert_to*` methods. + * - Encoding colors (compressing to store colors inside a less precision storage) + * should be done by invoking the `encode` and `decode` methods. + * - Changing alpha association should be done by invoking `premultiply_alpha` or + * `unpremultiply_alpha` methods. + * + * # Encoding. + * + * Color encoding is used to store colors with less precision as in using `uint8_t` in + * stead of `float`. This encoding is supported for `eSpace::SceneLinear`. + * To make this clear to the developer the `eSpace::SceneLinearByteEncoded` + * space is added. + * + * # Precision + * + * Colors can be stored using `uint8_t` or `float` colors. The conversion + * between the two precisions are available as methods. (`to_4b` and + * `to_4f`). + * + * # Alpha conversion + * + * Alpha conversion is only supported in SceneLinear space. + * + * Extending this file: + * - This file can be extended with `ColorHex/Hsl/Hsv` for different representations + * of rgb based colors. `ColorHsl4f<eSpace::SceneLinear, eAlpha::Premultiplied>` + * - Add non RGB spaces/storages ColorXyz. + */ + +/* Enumeration containing the different alpha modes. */ +enum class eAlpha { + /* Color and alpha are unassociated. */ + Straight, + /* Color and alpha are associated. */ + Premultiplied, +}; +std::ostream &operator<<(std::ostream &stream, const eAlpha &space); - Color4f() = default; +/* Enumeration containing internal spaces. */ +enum class eSpace { + /* Blender theme color space (sRGB). */ + Theme, + /* Blender internal scene linear color space (maps to SceneReference role in OCIO). */ + SceneLinear, + /* Blender internal scene linear color space compressed to be stored in 4 uint8_t. */ + SceneLinearByteEncoded, +}; +std::ostream &operator<<(std::ostream &stream, const eSpace &space); - Color4f(const float *rgba) : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) +/* Template class to store RGBA values with different precision, space and alpha association. */ +template<typename ChannelStorageType, eSpace Space, eAlpha Alpha> class ColorRGBA { + public: + ChannelStorageType r, g, b, a; + constexpr ColorRGBA() = default; + + constexpr ColorRGBA(const ChannelStorageType rgba[4]) + : r(rgba[0]), g(rgba[1]), b(rgba[2]), a(rgba[3]) { } - Color4f(float r, float g, float b, float a) : r(r), g(g), b(b), a(a) + constexpr ColorRGBA(const ChannelStorageType r, + const ChannelStorageType g, + const ChannelStorageType b, + const ChannelStorageType a) + : r(r), g(g), b(b), a(a) { } - operator float *() + operator ChannelStorageType *() { return &r; } - operator const float *() const + operator const ChannelStorageType *() const { return &r; } - friend std::ostream &operator<<(std::ostream &stream, Color4f c) + friend std::ostream &operator<<(std::ostream &stream, + const ColorRGBA<ChannelStorageType, Space, Alpha> &c) { - stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; + + stream << Space << Alpha << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; return stream; } - friend bool operator==(const Color4f &a, const Color4f &b) + friend bool operator==(const ColorRGBA<ChannelStorageType, Space, Alpha> &a, + const ColorRGBA<ChannelStorageType, Space, Alpha> &b) { return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } - friend bool operator!=(const Color4f &a, const Color4f &b) + friend bool operator!=(const ColorRGBA<ChannelStorageType, Space, Alpha> &a, + const ColorRGBA<ChannelStorageType, Space, Alpha> &b) { return !(a == b); } @@ -71,58 +152,209 @@ struct Color4f { } }; -struct Color4b { - uint8_t r, g, b, a; +/* Forward declarations of concrete color classes. */ +template<eAlpha Alpha> class ColorSceneLinear4f; +template<eAlpha Alpha> class ColorSceneLinearByteEncoded4b; +template<typename ChannelStorageType> class ColorTheme4; - Color4b() = default; +/* Forward declation of precision conversion methods. */ +BLI_INLINE ColorTheme4<float> BLI_color_convert_to_theme4f(const ColorTheme4<uint8_t> &srgb4b); +BLI_INLINE ColorTheme4<uint8_t> BLI_color_convert_to_theme4b(const ColorTheme4<float> &srgb4f); - Color4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : r(r), g(g), b(b), a(a) +template<eAlpha Alpha> +class ColorSceneLinear4f final : public ColorRGBA<float, eSpace::SceneLinear, Alpha> { + public: + constexpr ColorSceneLinear4f<Alpha>() : ColorRGBA<float, eSpace::SceneLinear, Alpha>() { } - Color4b(Color4f other) + constexpr ColorSceneLinear4f<Alpha>(const float *rgba) + : ColorRGBA<float, eSpace::SceneLinear, Alpha>(rgba) { - rgba_float_to_uchar(*this, other); } - operator Color4f() const + constexpr ColorSceneLinear4f<Alpha>(float r, float g, float b, float a) + : ColorRGBA<float, eSpace::SceneLinear, Alpha>(r, g, b, a) { - Color4f result; - rgba_uchar_to_float(result, *this); - return result; } - operator uint8_t *() + /** + * Convert to its byte encoded counter space. + **/ + ColorSceneLinearByteEncoded4b<Alpha> encode() const { - return &r; + ColorSceneLinearByteEncoded4b<Alpha> encoded; + linearrgb_to_srgb_uchar4(encoded, *this); + return encoded; } - operator const uint8_t *() const + /** + * Convert color and alpha association to premultiplied alpha. + * + * Does nothing when color has already a premultiplied alpha. + */ + ColorSceneLinear4f<eAlpha::Premultiplied> premultiply_alpha() const { - return &r; + if constexpr (Alpha == eAlpha::Straight) { + ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied; + straight_to_premul_v4_v4(premultiplied, *this); + return premultiplied; + } + else { + return *this; + } } - friend std::ostream &operator<<(std::ostream &stream, Color4b c) + /** + * Convert color and alpha association to straight alpha. + * + * Does nothing when color has straighten alpha. + */ + ColorSceneLinear4f<eAlpha::Straight> unpremultiply_alpha() const { - stream << "(" << c.r << ", " << c.g << ", " << c.b << ", " << c.a << ")"; - return stream; + if constexpr (Alpha == eAlpha::Premultiplied) { + ColorSceneLinear4f<eAlpha::Straight> straighten; + premul_to_straight_v4_v4(straighten, *this); + return straighten; + } + else { + return *this; + } } +}; - friend bool operator==(const Color4b &a, const Color4b &b) +template<eAlpha Alpha> +class ColorSceneLinearByteEncoded4b final + : public ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha> { + public: + constexpr ColorSceneLinearByteEncoded4b() = default; + + constexpr ColorSceneLinearByteEncoded4b(const uint8_t *rgba) + : ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(rgba) { - return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } - friend bool operator!=(const Color4b &a, const Color4b &b) + constexpr ColorSceneLinearByteEncoded4b(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + : ColorRGBA<uint8_t, eSpace::SceneLinearByteEncoded, Alpha>(r, g, b, a) { - return !(a == b); } - uint64_t hash() const + /** + * Convert to back to float color. + */ + ColorSceneLinear4f<Alpha> decode() const + { + ColorSceneLinear4f<Alpha> decoded; + srgb_to_linearrgb_uchar4(decoded, *this); + return decoded; + } +}; + +/** + * Theme color template class. + * + * Don't use directly, but use `ColorTheme4b/ColorTheme4b`. + * + * This has been implemented as a template to improve inlining. When implemented as concrete + * classes (ColorTheme4b/f) the functions would be hidden in a compile unit what wouldn't be + * inlined. + */ +template<typename ChannelStorageType> +class ColorTheme4 final : public ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight> { + public: + constexpr ColorTheme4() : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(){}; + + constexpr ColorTheme4(const ChannelStorageType *rgba) + : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(rgba) + { + } + + constexpr ColorTheme4(ChannelStorageType r, + ChannelStorageType g, + ChannelStorageType b, + ChannelStorageType a) + : ColorRGBA<ChannelStorageType, eSpace::Theme, eAlpha::Straight>(r, g, b, a) + { + } + + /** + * Change precision of color to float. + */ + ColorTheme4<float> to_4f() const { - return static_cast<uint64_t>(r * 1283591) ^ static_cast<uint64_t>(g * 850177) ^ - static_cast<uint64_t>(b * 735391) ^ static_cast<uint64_t>(a * 442319); + if constexpr ((std::is_same_v<ChannelStorageType, uint8_t>)) { + return BLI_color_convert_to_theme4f(*this); + } + else { + return *this; + } + } + + /** + * Change precision of color to uint8_t. + */ + ColorTheme4<uint8_t> to_4b() const + { + if constexpr ((std::is_same_v<ChannelStorageType, float>)) { + return BLI_color_convert_to_theme4b(*this); + } + else { + return *this; + } } }; +using ColorTheme4b = ColorTheme4<uint8_t>; +using ColorTheme4f = ColorTheme4<float>; + +BLI_INLINE ColorTheme4b BLI_color_convert_to_theme4b(const ColorTheme4f &theme4f) +{ + ColorTheme4b theme4b; + rgba_float_to_uchar(theme4b, theme4f); + return theme4b; +} + +BLI_INLINE ColorTheme4f BLI_color_convert_to_theme4f(const ColorTheme4b &theme4b) +{ + ColorTheme4f theme4f; + rgba_uchar_to_float(theme4f, theme4b); + return theme4f; +} + +BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> BLI_color_convert_to_scene_linear( + const ColorTheme4f &theme4f) +{ + ColorSceneLinear4f<eAlpha::Straight> scene_linear; + srgb_to_linearrgb_v4(scene_linear, theme4f); + return scene_linear; +} + +BLI_INLINE ColorSceneLinear4f<eAlpha::Straight> BLI_color_convert_to_scene_linear( + const ColorTheme4b &theme4b) +{ + ColorSceneLinear4f<eAlpha::Straight> scene_linear; + srgb_to_linearrgb_uchar4(scene_linear, theme4b); + return scene_linear; +} + +BLI_INLINE ColorTheme4f +BLI_color_convert_to_theme4f(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear) +{ + ColorTheme4f theme4f; + linearrgb_to_srgb_v4(theme4f, scene_linear); + return theme4f; +} + +BLI_INLINE ColorTheme4b +BLI_color_convert_to_theme4b(const ColorSceneLinear4f<eAlpha::Straight> &scene_linear) +{ + ColorTheme4b theme4b; + linearrgb_to_srgb_uchar4(theme4b, scene_linear); + return theme4b; +} + +/* Internal roles. For convenience to shorten the type names and hide complexity. */ +using ColorGeometry4f = ColorSceneLinear4f<eAlpha::Premultiplied>; +using ColorGeometry4b = ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied>; + } // namespace blender diff --git a/source/blender/blenlib/BLI_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h index 680c4bc78da..4b5a7d671f2 100644 --- a/source/blender/blenlib/BLI_compiler_attrs.h +++ b/source/blender/blenlib/BLI_compiler_attrs.h @@ -98,3 +98,10 @@ #else # define ATTR_ALIGN(x) __attribute__((aligned(x))) #endif + +/* Alignment directive */ +#ifdef _WIN64 +# define ALIGN_STRUCT __declspec(align(64)) +#else +# define ALIGN_STRUCT +#endif diff --git a/source/blender/blenlib/BLI_enumerable_thread_specific.hh b/source/blender/blenlib/BLI_enumerable_thread_specific.hh new file mode 100644 index 00000000000..89be4cad848 --- /dev/null +++ b/source/blender/blenlib/BLI_enumerable_thread_specific.hh @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#ifdef WITH_TBB +# include <tbb/enumerable_thread_specific.h> +#endif + +#include <atomic> +#include <mutex> + +#include "BLI_map.hh" +#include "BLI_utility_mixins.hh" + +namespace blender { + +namespace enumerable_thread_specific_utils { +inline std::atomic<int> next_id = 0; +inline thread_local int thread_id = next_id.fetch_add(1, std::memory_order_relaxed); +} // namespace enumerable_thread_specific_utils + +/** + * This is mainly a wrapper for `tbb::enumerable_thread_specific`. The wrapper is needed because we + * want to be able to build without tbb. + * + * More features of the tbb version can be wrapped when they are used. + */ +template<typename T> class EnumerableThreadSpecific : NonCopyable, NonMovable { +#ifdef WITH_TBB + + private: + tbb::enumerable_thread_specific<T> values_; + + public: + T &local() + { + return values_.local(); + } + +#else /* WITH_TBB */ + + private: + std::mutex mutex_; + /* Maps thread ids to their corresponding values. The values are not embedded in the map, so that + * their addresses do not change when the map grows. */ + Map<int, std::unique_ptr<T>> values_; + + public: + T &local() + { + const int thread_id = enumerable_thread_specific_utils::thread_id; + std::lock_guard lock{mutex_}; + return *values_.lookup_or_add_cb(thread_id, []() { return std::make_unique<T>(); }); + } + +#endif /* WITH_TBB */ +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_fileops.h b/source/blender/blenlib/BLI_fileops.h index df80e720363..bf18dd1070e 100644 --- a/source/blender/blenlib/BLI_fileops.h +++ b/source/blender/blenlib/BLI_fileops.h @@ -87,7 +87,7 @@ typedef enum eFileAttributes { FILE_ATTR_RESTRICTED = 1 << 6, /* Protected by OS. */ FILE_ATTR_TEMPORARY = 1 << 7, /* Used for temporary storage. */ FILE_ATTR_SPARSE_FILE = 1 << 8, /* Sparse File. */ - FILE_ATTR_OFFLINE = 1 << 9, /* Data is not immediately available. */ + FILE_ATTR_OFFLINE = 1 << 9, /* Contents available after a short delay. */ FILE_ATTR_ALIAS = 1 << 10, /* Mac Alias or Windows LNK. File-based redirection. */ FILE_ATTR_REPARSE_POINT = 1 << 11, /* File has associated re-parse point. */ FILE_ATTR_SYMLINK = 1 << 12, /* Reference to another file. */ diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index cbc4d4ed366..04aae375889 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -245,6 +245,13 @@ struct float3 { return result; } + static float3 cross(const float3 &a, const float3 &b) + { + float3 result; + cross_v3_v3v3(result, a, b); + return result; + } + static float3 project(const float3 &a, const float3 &b) { float3 result; diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index f5ba91bc12c..396b0b1bd21 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -45,6 +45,37 @@ struct float4x4 { return mat; } + static float4x4 from_normalized_axis_data(const float3 location, + const float3 forward, + const float3 up) + { + BLI_ASSERT_UNIT_V3(forward); + BLI_ASSERT_UNIT_V3(up); + float4x4 matrix; + const float3 cross = float3::cross(forward, up); + matrix.values[0][0] = forward.x; + matrix.values[1][0] = cross.x; + matrix.values[2][0] = up.x; + matrix.values[3][0] = location.x; + + matrix.values[0][1] = forward.y; + matrix.values[1][1] = cross.y; + matrix.values[2][1] = up.y; + matrix.values[3][1] = location.y; + + matrix.values[0][2] = forward.z; + matrix.values[1][2] = cross.z; + matrix.values[2][2] = up.z; + matrix.values[3][2] = location.z; + + matrix.values[0][3] = 0.0f; + matrix.values[1][3] = 0.0f; + matrix.values[2][3] = 0.0f; + matrix.values[3][3] = 1.0f; + + return matrix; + } + static float4x4 identity() { float4x4 mat; @@ -116,6 +147,19 @@ struct float4x4 { return scale; } + void apply_scale(const float scale) + { + values[0][0] *= scale; + values[0][1] *= scale; + values[0][2] *= scale; + values[1][0] *= scale; + values[1][1] *= scale; + values[1][2] *= scale; + values[2][0] *= scale; + values[2][1] *= scale; + values[2][2] *= scale; + } + float4x4 inverted() const { float4x4 result; diff --git a/source/blender/blenlib/BLI_hash.hh b/source/blender/blenlib/BLI_hash.hh index 4022c2baa1f..fbed321534c 100644 --- a/source/blender/blenlib/BLI_hash.hh +++ b/source/blender/blenlib/BLI_hash.hh @@ -85,9 +85,12 @@ namespace blender { /** - * If there is no other specialization of #DefaultHash for a given type, try to call `hash()` on - * the value. If there is no such method, this will result in a compiler error. Usually that means - * that you have to implement a hash function using one of three strategies listed above. + * If there is no other specialization of #DefaultHash for a given type, look for a hash function + * on the type itself. Implementing a `hash()` method on a type is often significantly easier than + * specializing #DefaultHash. + * + * To support heterogeneous lookup, a type can also implement a static `hash_as(const OtherType &)` + * function. * * In the case of an enum type, the default hash is just to cast the enum value to an integer. */ @@ -95,12 +98,25 @@ template<typename T> struct DefaultHash { uint64_t operator()(const T &value) const { if constexpr (std::is_enum_v<T>) { + /* For enums use the value as hash directly. */ return (uint64_t)value; } else { + /* Try to call the `hash()` function on the value. */ + /* If this results in a compiler error, no hash function for the type has been found. */ return value.hash(); } } + + template<typename U> uint64_t operator()(const U &value) const + { + /* Try calling the static `T::hash_as(value)` function with the given value. The returned hash + * should be "compatible" with `T::hash()`. Usually that means that if `value` is converted to + * `T` its hash does not change. */ + /* If this results in a compiler error, no hash function for the heterogeneous lookup has been + * found. */ + return T::hash_as(value); + } }; /** diff --git a/source/blender/blenlib/BLI_linear_allocator.hh b/source/blender/blenlib/BLI_linear_allocator.hh index 6aa97d5c5e7..7de6bcfdd98 100644 --- a/source/blender/blenlib/BLI_linear_allocator.hh +++ b/source/blender/blenlib/BLI_linear_allocator.hh @@ -129,6 +129,21 @@ template<typename Allocator = GuardedAllocator> class LinearAllocator : NonCopya } /** + * Construct multiple instances of a type in an array. The constructor of is called with the + * given arguments. The caller is responsible for calling the destructor (and not `delete`) on + * the constructed elements. + */ + template<typename T, typename... Args> + MutableSpan<T> construct_array(int64_t size, Args &&... args) + { + MutableSpan<T> array = this->allocate_array<T>(size); + for (const int64_t i : IndexRange(size)) { + new (&array[i]) T(std::forward<Args>(args)...); + } + return array; + } + + /** * Copy the given array into a memory buffer provided by this allocator. */ template<typename T> MutableSpan<T> construct_array_copy(Span<T> src) diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index 376bf5df06f..4d254960f34 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -605,6 +605,37 @@ class Map { } /** + * Returns the key that is stored in the set that compares equal to the given key. This invokes + * undefined behavior when the key is not in the map. + */ + const Key &lookup_key(const Key &key) const + { + return this->lookup_key_as(key); + } + template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const + { + const Slot &slot = this->lookup_slot(key, hash_(key)); + return *slot.key(); + } + + /** + * Returns a pointer to the key that is stored in the map that compares equal to the given key. + * If the key is not in the map, null is returned. + */ + const Key *lookup_key_ptr(const Key &key) const + { + return this->lookup_key_ptr_as(key); + } + template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const + { + const Slot *slot = this->lookup_slot_ptr(key, hash_(key)); + if (slot == nullptr) { + return nullptr; + } + return slot->key(); + } + + /** * Calls the provided callback for every key-value-pair in the map. The callback is expected * to take a `const Key &` as first and a `const Value &` as second parameter. */ @@ -621,19 +652,23 @@ class Map { } } - /** - * A utility iterator that reduces the amount of code when implementing the actual iterators. - * This uses the "curiously recurring template pattern" (CRTP). - */ - template<typename SubIterator> struct BaseIterator { + /* Common base class for all iterators below. */ + struct BaseIterator { + public: using iterator_category = std::forward_iterator_tag; using difference_type = std::ptrdiff_t; + protected: + /* We could have separate base iterators for const and non-const iterators, but that would add + * more complexity than benefits right now. */ Slot *slots_; int64_t total_slots_; int64_t current_slot_; - BaseIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) + friend Map; + + public: + BaseIterator(const Slot *slots, const int64_t total_slots, const int64_t current_slot) : slots_(const_cast<Slot *>(slots)), total_slots_(total_slots), current_slot_(current_slot) { } @@ -667,11 +702,29 @@ class Map { return !(a != b); } + protected: + Slot ¤t_slot() const + { + return slots_[current_slot_]; + } + }; + + /** + * A utility iterator that reduces the amount of code when implementing the actual iterators. + * This uses the "curiously recurring template pattern" (CRTP). + */ + template<typename SubIterator> class BaseIteratorRange : public BaseIterator { + public: + BaseIteratorRange(const Slot *slots, int64_t total_slots, int64_t current_slot) + : BaseIterator(slots, total_slots, current_slot) + { + } + SubIterator begin() const { - for (int64_t i = 0; i < total_slots_; i++) { - if (slots_[i].is_occupied()) { - return SubIterator(slots_, total_slots_, i); + for (int64_t i = 0; i < this->total_slots_; i++) { + if (this->slots_[i].is_occupied()) { + return SubIterator(this->slots_, this->total_slots_, i); } } return this->end(); @@ -679,23 +732,18 @@ class Map { SubIterator end() const { - return SubIterator(slots_, total_slots_, total_slots_); - } - - Slot ¤t_slot() const - { - return slots_[current_slot_]; + return SubIterator(this->slots_, this->total_slots_, this->total_slots_); } }; - class KeyIterator final : public BaseIterator<KeyIterator> { + class KeyIterator final : public BaseIteratorRange<KeyIterator> { public: using value_type = Key; using pointer = const Key *; using reference = const Key &; KeyIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<KeyIterator>(slots, total_slots, current_slot) + : BaseIteratorRange<KeyIterator>(slots, total_slots, current_slot) { } @@ -705,14 +753,14 @@ class Map { } }; - class ValueIterator final : public BaseIterator<ValueIterator> { + class ValueIterator final : public BaseIteratorRange<ValueIterator> { public: using value_type = Value; using pointer = const Value *; using reference = const Value &; ValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<ValueIterator>(slots, total_slots, current_slot) + : BaseIteratorRange<ValueIterator>(slots, total_slots, current_slot) { } @@ -722,14 +770,14 @@ class Map { } }; - class MutableValueIterator final : public BaseIterator<MutableValueIterator> { + class MutableValueIterator final : public BaseIteratorRange<MutableValueIterator> { public: using value_type = Value; using pointer = Value *; using reference = Value &; - MutableValueIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<MutableValueIterator>(slots, total_slots, current_slot) + MutableValueIterator(Slot *slots, int64_t total_slots, int64_t current_slot) + : BaseIteratorRange<MutableValueIterator>(slots, total_slots, current_slot) { } @@ -754,14 +802,14 @@ class Map { } }; - class ItemIterator final : public BaseIterator<ItemIterator> { + class ItemIterator final : public BaseIteratorRange<ItemIterator> { public: using value_type = Item; using pointer = Item *; using reference = Item &; ItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<ItemIterator>(slots, total_slots, current_slot) + : BaseIteratorRange<ItemIterator>(slots, total_slots, current_slot) { } @@ -772,14 +820,14 @@ class Map { } }; - class MutableItemIterator final : public BaseIterator<MutableItemIterator> { + class MutableItemIterator final : public BaseIteratorRange<MutableItemIterator> { public: using value_type = MutableItem; using pointer = MutableItem *; using reference = MutableItem &; - MutableItemIterator(const Slot *slots, int64_t total_slots, int64_t current_slot) - : BaseIterator<MutableItemIterator>(slots, total_slots, current_slot) + MutableItemIterator(Slot *slots, int64_t total_slots, int64_t current_slot) + : BaseIteratorRange<MutableItemIterator>(slots, total_slots, current_slot) { } @@ -840,6 +888,19 @@ class Map { } /** + * Remove the key-value-pair that the iterator is currently pointing at. + * It is valid to call this method while iterating over the map. However, after this method has + * been called, the removed element must not be accessed anymore. + */ + void remove(const BaseIterator &iterator) + { + Slot &slot = iterator.current_slot(); + BLI_assert(slot.is_occupied()); + slot.remove(); + removed_slots_++; + } + + /** * Print common statistics like size and collision count. This is useful for debugging purposes. */ void print_stats(StringRef name = "") const diff --git a/source/blender/blenlib/BLI_math_color.h b/source/blender/blenlib/BLI_math_color.h index 26d2f1fcb29..28257ba418a 100644 --- a/source/blender/blenlib/BLI_math_color.h +++ b/source/blender/blenlib/BLI_math_color.h @@ -105,8 +105,8 @@ int constrain_rgb(float *r, float *g, float *b); void minmax_rgb(short c[3]); void hsv_clamp_v(float hsv[3], float v_max); -void rgb_float_set_hue_float_offset(float *rgb, float hue_offset); -void rgb_byte_set_hue_float_offset(unsigned char *rgb, float hue_offset); +void rgb_float_set_hue_float_offset(float rgb[3], float hue_offset); +void rgb_byte_set_hue_float_offset(unsigned char rgb[3], float hue_offset); void rgb_uchar_to_float(float r_col[3], const unsigned char col_ub[3]); void rgba_uchar_to_float(float r_col[4], const unsigned char col_ub[4]); @@ -143,6 +143,7 @@ MINLINE void rgba_uchar_args_test_set(unsigned char col[4], MINLINE void cpack_cpy_3ub(unsigned char r_col[3], const unsigned int pack); void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, float max); +void wavelength_to_xyz_table(float *r_table, int width); /********* lift/gamma/gain / ASC-CDL conversion ***********/ diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index d767c2924d1..c744c5d13d3 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -119,10 +119,10 @@ float dist_signed_to_plane_v3(const float p[3], const float plane[4]); float dist_to_plane_v3(const float p[3], const float plane[4]); /* plane3 versions */ -float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[4]); -float dist_squared_to_plane3_v3(const float p[3], const float plane[4]); -float dist_signed_to_plane3_v3(const float p[3], const float plane[4]); -float dist_to_plane3_v3(const float p[3], const float plane[4]); +float dist_signed_squared_to_plane3_v3(const float p[3], const float plane[3]); +float dist_squared_to_plane3_v3(const float p[3], const float plane[3]); +float dist_signed_to_plane3_v3(const float p[3], const float plane[3]); +float dist_to_plane3_v3(const float p[3], const float plane[3]); float dist_squared_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]); float dist_to_line_segment_v3(const float p[3], const float l1[3], const float l2[3]); @@ -778,7 +778,7 @@ MINLINE float dot_shsh(const float a[9], const float b[9]); MINLINE float eval_shv3(float r[9], const float v[3]); MINLINE float diffuse_shv3(float r[9], const float v[3]); MINLINE void vec_fac_to_sh(float r[9], const float v[3], const float f); -MINLINE void madd_sh_shfl(float r[9], const float sh[3], const float f); +MINLINE void madd_sh_shfl(float r[9], const float sh[9], const float f); /********************************* Form Factor *******************************/ diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index 378095589e8..54df88ca541 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -362,7 +362,7 @@ void loc_quat_size_to_mat4(float R[4][4], const float size[3]); void loc_axisangle_size_to_mat4(float R[4][4], const float loc[3], - const float axis[4], + const float axis[3], const float angle, const float size[3]); diff --git a/source/blender/blenlib/BLI_math_solvers.h b/source/blender/blenlib/BLI_math_solvers.h index 13481e27e2a..39a79efc7e2 100644 --- a/source/blender/blenlib/BLI_math_solvers.h +++ b/source/blender/blenlib/BLI_math_solvers.h @@ -41,7 +41,7 @@ bool BLI_eigen_solve_selfadjoint_m3(const float m3[3][3], float r_eigen_values[3], float r_eigen_vectors[3][3]); -void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[], float r_V[3][3]); +void BLI_svd_m3(const float m3[3][3], float r_U[3][3], float r_S[3], float r_V[3][3]); /***************************** Simple Solvers ************************************/ diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index bb1e1a1c38d..b43f86af670 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -146,7 +146,7 @@ MINLINE void mul_v3_v3(float r[3], const float a[3]); MINLINE void mul_v3_v3v3(float r[3], const float a[3], const float b[3]); MINLINE void mul_v4_fl(float r[4], float f); MINLINE void mul_v4_v4(float r[4], const float a[4]); -MINLINE void mul_v4_v4fl(float r[3], const float a[4], float f); +MINLINE void mul_v4_v4fl(float r[4], const float a[4], float f); MINLINE void mul_v2_v2_cw(float r[2], const float mat[2], const float vec[2]); MINLINE void mul_v2_v2_ccw(float r[2], const float mat[2], const float vec[2]); MINLINE float mul_project_m4_v3_zfac(const float mat[4][4], @@ -177,7 +177,7 @@ MINLINE void negate_v2_v2(float r[2], const float a[2]); MINLINE void negate_v3(float r[3]); MINLINE void negate_v3_v3(float r[3], const float a[3]); MINLINE void negate_v4(float r[4]); -MINLINE void negate_v4_v4(float r[4], const float a[3]); +MINLINE void negate_v4_v4(float r[4], const float a[4]); MINLINE void negate_v3_short(short r[3]); MINLINE void negate_v3_db(double r[3]); @@ -323,11 +323,11 @@ void flip_v2_v2v2(float v[2], const float v1[2], const float v2[2]); /********************************* Comparison ********************************/ -MINLINE bool is_zero_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT; +MINLINE bool is_zero_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_zero_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; MINLINE bool is_zero_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; -bool is_finite_v2(const float a[3]) ATTR_WARN_UNUSED_RESULT; +bool is_finite_v2(const float a[2]) ATTR_WARN_UNUSED_RESULT; bool is_finite_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; bool is_finite_v4(const float a[4]) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/BLI_mesh_intersect.hh b/source/blender/blenlib/BLI_mesh_intersect.hh index a7996939bb1..7000349c5af 100644 --- a/source/blender/blenlib/BLI_mesh_intersect.hh +++ b/source/blender/blenlib/BLI_mesh_intersect.hh @@ -357,6 +357,9 @@ IMesh trimesh_nary_intersect(const IMesh &tm_in, bool use_self, IMeshArena *arena); +/** Return an IMesh that is a triangulation of a mesh with general polygonal faces. */ +IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena); + /** This has the side effect of populating verts in the #IMesh. */ void write_obj_mesh(IMesh &m, const std::string &objname); diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index c32ba0826df..c3876d4eaf8 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -662,6 +662,16 @@ template<typename T> class MutableSpan { } /** + * Return a reference to the first element in the array. This invokes undefined behavior when the + * array is empty. + */ + constexpr T &first() const + { + BLI_assert(size_ > 0); + return data_[0]; + } + + /** * Returns a reference to the last element. This invokes undefined behavior when the array is * empty. */ diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index d08a5c65c52..8bea2584ca7 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -669,6 +669,21 @@ class Vector { } /** + * Return a reference to the first element in the vector. + * This invokes undefined behavior when the vector is empty. + */ + const T &first() const + { + BLI_assert(this->size() > 0); + return *begin_; + } + T &first() + { + BLI_assert(this->size() > 0); + return *begin_; + } + + /** * Return how many values are currently stored in the vector. */ int64_t size() const @@ -725,11 +740,12 @@ class Vector { BLI_assert(index >= 0); BLI_assert(index < this->size()); T *element_to_remove = begin_ + index; - if (element_to_remove < end_) { - *element_to_remove = std::move(*(end_ - 1)); + T *last_element = end_ - 1; + if (element_to_remove < last_element) { + *element_to_remove = std::move(*last_element); } - end_--; - end_->~T(); + end_ = last_element; + last_element->~T(); UPDATE_VECTOR_SIZE(this); } diff --git a/source/blender/blenlib/BLI_vector_set.hh b/source/blender/blenlib/BLI_vector_set.hh index 14b38d564cb..567e4fd8128 100644 --- a/source/blender/blenlib/BLI_vector_set.hh +++ b/source/blender/blenlib/BLI_vector_set.hh @@ -398,6 +398,55 @@ class VectorSet { } /** + * Return the index of the key in the vector. If the key is not in the set, add it and return its + * index. + */ + int64_t index_of_or_add(const Key &key) + { + return this->index_of_or_add_as(key); + } + int64_t index_of_or_add(Key &&key) + { + return this->index_of_or_add_as(std::move(key)); + } + template<typename ForwardKey> int64_t index_of_or_add_as(ForwardKey &&key) + { + return this->index_of_or_add__impl(std::forward<ForwardKey>(key), hash_(key)); + } + + /** + * Returns the key that is stored in the vector set that compares equal to the given key. This + * invokes undefined behavior when the key is not in the set. + */ + const Key &lookup_key(const Key &key) const + { + return this->lookup_key_as(key); + } + template<typename ForwardKey> const Key &lookup_key_as(const ForwardKey &key) const + { + const Key *key_ptr = this->lookup_key_ptr_as(key); + BLI_assert(key_ptr != nullptr); + return *key_ptr; + } + + /** + * Returns a pointer to the key that is stored in the vector set that compares equal to the given + * key. If the key is not in the set, null is returned. + */ + const Key *lookup_key_ptr(const Key &key) const + { + return this->lookup_key_ptr_as(key); + } + template<typename ForwardKey> const Key *lookup_key_ptr_as(const ForwardKey &key) const + { + const int64_t index = this->index_of_try__impl(key, hash_(key)); + if (index >= 0) { + return keys_ + index; + } + return nullptr; + } + + /** * Get a pointer to the beginning of the array containing all keys. */ const Key *data() const @@ -484,6 +533,14 @@ class VectorSet { } /** + * Remove all keys from the vector set. + */ + void clear() + { + this->noexcept_reset(); + } + + /** * Get the number of collisions that the probing strategy has to go through to find the key or * determine that it is not in the set. */ @@ -652,6 +709,26 @@ class VectorSet { VECTOR_SET_SLOT_PROBING_END(); } + template<typename ForwardKey> + int64_t index_of_or_add__impl(ForwardKey &&key, const uint64_t hash) + { + this->ensure_can_add(); + + VECTOR_SET_SLOT_PROBING_BEGIN (hash, slot) { + if (slot.contains(key, is_equal_, hash, keys_)) { + return slot.index(); + } + if (slot.is_empty()) { + const int64_t index = this->size(); + new (keys_ + index) Key(std::forward<ForwardKey>(key)); + slot.occupy(index, hash); + occupied_and_removed_slots_++; + return index; + } + } + VECTOR_SET_SLOT_PROBING_END(); + } + Key pop__impl() { BLI_assert(this->size() > 0); diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index eae15f0300c..1c02bce8411 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -38,6 +38,7 @@ */ #include "BLI_array.hh" +#include "BLI_index_mask.hh" #include "BLI_span.hh" namespace blender { @@ -127,14 +128,25 @@ template<typename T> class VArray { /* Copy the entire virtual array into a span. */ void materialize(MutableSpan<T> r_span) const { - BLI_assert(size_ == r_span.size()); - this->materialize_impl(r_span); + this->materialize(IndexMask(size_), r_span); + } + + /* Copy some indices of the virtual array into a span. */ + void materialize(IndexMask mask, MutableSpan<T> r_span) const + { + BLI_assert(mask.min_array_size() <= size_); + this->materialize_impl(mask, r_span); } void materialize_to_uninitialized(MutableSpan<T> r_span) const { - BLI_assert(size_ == r_span.size()); - this->materialize_to_uninitialized_impl(r_span); + this->materialize_to_uninitialized(IndexMask(size_), r_span); + } + + void materialize_to_uninitialized(IndexMask mask, MutableSpan<T> r_span) const + { + BLI_assert(mask.min_array_size() <= size_); + this->materialize_to_uninitialized_impl(mask, r_span); } protected: @@ -164,40 +176,35 @@ template<typename T> class VArray { return T(); } - virtual void materialize_impl(MutableSpan<T> r_span) const + virtual void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const { + T *dst = r_span.data(); if (this->is_span()) { - const Span<T> span = this->get_internal_span(); - initialized_copy_n(span.data(), size_, r_span.data()); + const T *src = this->get_internal_span().data(); + mask.foreach_index([&](const int64_t i) { dst[i] = src[i]; }); } else if (this->is_single()) { const T single = this->get_internal_single(); - initialized_fill_n(r_span.data(), size_, single); + mask.foreach_index([&](const int64_t i) { dst[i] = single; }); } else { - const int64_t size = size_; - for (int64_t i = 0; i < size; i++) { - r_span[i] = this->get(i); - } + mask.foreach_index([&](const int64_t i) { dst[i] = this->get(i); }); } } - virtual void materialize_to_uninitialized_impl(MutableSpan<T> r_span) const + virtual void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const { + T *dst = r_span.data(); if (this->is_span()) { - const Span<T> span = this->get_internal_span(); - uninitialized_copy_n(span.data(), size_, r_span.data()); + const T *src = this->get_internal_span().data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(src[i]); }); } else if (this->is_single()) { const T single = this->get_internal_single(); - uninitialized_fill_n(r_span.data(), size_, single); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(single); }); } else { - const int64_t size = size_; - T *dst = r_span.data(); - for (int64_t i = 0; i < size; i++) { - new (dst + i) T(this->get(i)); - } + mask.foreach_index([&](const int64_t i) { new (dst + i) T(this->get(i)); }); } } }; @@ -494,6 +501,18 @@ template<typename T, typename GetFunc> class VArray_For_Func final : public VArr { return get_func_(index); } + + void materialize_impl(IndexMask mask, MutableSpan<T> r_span) const override + { + T *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { dst[i] = get_func_(i); }); + } + + void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<T> r_span) const override + { + T *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) T(get_func_(i)); }); + } }; template<typename StructT, typename ElemT, ElemT (*GetFunc)(const StructT &)> @@ -511,6 +530,18 @@ class VArray_For_DerivedSpan : public VArray<ElemT> { { return GetFunc(data_[index]); } + + void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); }); + } + + void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); }); + } }; template<typename StructT, @@ -537,6 +568,18 @@ class VMutableArray_For_DerivedSpan : public VMutableArray<ElemT> { { SetFunc(data_[index], std::move(value)); } + + void materialize_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { dst[i] = GetFunc(data_[i]); }); + } + + void materialize_to_uninitialized_impl(IndexMask mask, MutableSpan<ElemT> r_span) const override + { + ElemT *dst = r_span.data(); + mask.foreach_index([&](const int64_t i) { new (dst + i) ElemT(GetFunc(data_[i])); }); + } }; /** diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e66049c9bd6..f3dc343ee20 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -39,6 +39,7 @@ set(SRC intern/BLI_args.c intern/BLI_array.c intern/BLI_assert.c + intern/BLI_color.cc intern/BLI_dial_2d.c intern/BLI_dynstr.c intern/BLI_filelist.c @@ -185,6 +186,7 @@ set(SRC BLI_edgehash.h BLI_endian_switch.h BLI_endian_switch_inline.h + BLI_enumerable_thread_specific.hh BLI_expr_pylike_eval.h BLI_fileops.h BLI_fileops_types.h @@ -388,6 +390,7 @@ if(WITH_GTESTS) tests/BLI_array_store_test.cc tests/BLI_array_test.cc tests/BLI_array_utils_test.cc + tests/BLI_color_test.cc tests/BLI_delaunay_2d_test.cc tests/BLI_disjoint_set_test.cc tests/BLI_edgehash_test.cc diff --git a/source/blender/blenlib/intern/BLI_color.cc b/source/blender/blenlib/intern/BLI_color.cc new file mode 100644 index 00000000000..6dcef4f4688 --- /dev/null +++ b/source/blender/blenlib/intern/BLI_color.cc @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_color.hh" + +namespace blender { + +std::ostream &operator<<(std::ostream &stream, const eAlpha &space) +{ + switch (space) { + case eAlpha::Straight: { + stream << "Straight"; + break; + } + case eAlpha::Premultiplied: { + stream << "Premultiplied"; + break; + } + } + return stream; +} + +std::ostream &operator<<(std::ostream &stream, const eSpace &space) +{ + switch (space) { + case eSpace::Theme: { + stream << "Theme"; + break; + } + case eSpace::SceneLinear: { + stream << "SceneLinear"; + break; + } + case eSpace::SceneLinearByteEncoded: { + stream << "SceneLinearByteEncoded"; + break; + } + } + return stream; +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/BLI_dial_2d.c b/source/blender/blenlib/intern/BLI_dial_2d.c index b5f1d7818cf..786fdd219a3 100644 --- a/source/blender/blenlib/intern/BLI_dial_2d.c +++ b/source/blender/blenlib/intern/BLI_dial_2d.c @@ -78,7 +78,7 @@ float BLI_dial_angle(Dial *dial, const float current_position[2]) cosval = dot_v2v2(current_direction, dial->initial_direction); sinval = cross_v2v2(current_direction, dial->initial_direction); - /* clamp to avoid nans in #acos */ + /* Clamp to avoid NAN's in #acos */ angle = atan2f(sinval, cosval); /* change of sign, we passed the 180 degree threshold. This means we need to add a turn. diff --git a/source/blender/blenlib/intern/math_color.c b/source/blender/blenlib/intern/math_color.c index 8fd2802a547..abcb3139dc7 100644 --- a/source/blender/blenlib/intern/math_color.c +++ b/source/blender/blenlib/intern/math_color.c @@ -713,3 +713,75 @@ void blackbody_temperature_to_rgb_table(float *r_table, int width, float min, fl r_table[i * 4 + 3] = 0.0f; } } + +/* ****************************** wavelength ******************************** */ +/* Wavelength to RGB. */ + +/* CIE colour matching functions xBar, yBar, and zBar for + * wavelengths from 380 through 780 nanometers, every 5 + * nanometers. + * For a wavelength lambda in this range: + * cie_colour_match[(lambda - 380) / 5][0] = xBar + * cie_colour_match[(lambda - 380) / 5][1] = yBar + * cie_colour_match[(lambda - 380) / 5][2] = zBar */ + +static float cie_colour_match[81][3] = { + {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f}, + {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f}, + {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f}, + {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f}, + {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f}, + {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f}, + {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f}, + {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f}, + {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f}, + {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f}, + {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f}, + {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f}, + {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f}, + {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f}, + {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f}, + {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f}, + {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f}, + {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f}, + {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f}, + {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f}, + {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f}, + {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f}, + {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f}, + {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f}, + {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f}, + {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, + {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}}; + +static void wavelength_to_xyz(float xyz[3], float lambda_nm) +{ + float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); /* Scaled 0..80. */ + int i = (int)ii; + + if (i < 0 || i >= 80) { + xyz[0] = 0.0f; + xyz[1] = 0.0f; + xyz[2] = 0.0f; + } + else { + ii -= (float)i; + const float *c = cie_colour_match[i]; + xyz[0] = c[0] + ii * (c[3] - c[0]); + xyz[1] = c[1] + ii * (c[4] - c[1]); + xyz[2] = c[2] + ii * (c[5] - c[2]); + } +} + +void wavelength_to_xyz_table(float *r_table, int width) +{ + for (int i = 0; i < width; i++) { + float temperature = 380 + 400 / (float)width * (float)i; + + float rgb[3]; + wavelength_to_xyz(rgb, temperature); + + copy_v3_v3(&r_table[i * 4], rgb); + r_table[i * 4 + 3] = 0.0f; + } +} diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 01cda6c9e4a..508de506ae8 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -2353,7 +2353,7 @@ bool isect_planes_v3_fn( for (i_test = 0; i_test < planes_len; i_test++) { const float *np_test = planes[i_test]; if (((dot_v3v3(np_test, co_test) + np_test[3]) > eps_isect)) { - /* For low epsilon values the point could intersect it's own plane. */ + /* For low epsilon values the point could intersect its own plane. */ if (!ELEM(i_test, i, j, k)) { break; } @@ -5393,7 +5393,7 @@ void accumulate_vertex_normals_poly_v3(float **vertnos, void tangent_from_uv_v3(const float uv1[2], const float uv2[2], - const float uv3[3], + const float uv3[2], const float co1[3], const float co2[3], const float co3[3], diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index a21e0c8f092..fb7b96fde78 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -412,7 +412,7 @@ bool is_finite_v4(const float v[4]) * this would return the angle at the elbow. * * note that when v1/v2/v3 represent 3 points along a straight line - * that the angle returned will be pi (180deg), rather then 0.0 + * that the angle returned will be pi (180deg), rather than 0.0 */ float angle_v3v3v3(const float a[3], const float b[3], const float c[3]) { diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 51b88e80600..25291b8c3b0 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -38,7 +38,6 @@ # include "BLI_math_mpq.hh" # include "BLI_mesh_intersect.hh" # include "BLI_mpq3.hh" -# include "BLI_polyfill_2d.h" # include "BLI_set.hh" # include "BLI_span.hh" # include "BLI_stack.hh" @@ -2680,211 +2679,10 @@ static IMesh raycast_patches_boolean(const IMesh &tm, } } BLI_bvhtree_free(tree); - ans.set_faces(out_faces); - return ans; -} - -# ifdef fast_triangulate -/* This code uses Blenlib's BLI_polyfill_calc to do triangulation, and is therefore quite fast. - * Unfortunately, it can product degenerate triangles that mesh_intersect will remove, leaving - * the mesh non-PWN. - */ - -/** - * Tessellate face f into triangles and return an array of `const Face *` - * giving that triangulation. Intended to be used when f has > 4 vertices. - * Care is taken so that the original edge index associated with - * each edge in the output triangles either matches the original edge - * for the (identical) edge of f, or else is -1. So diagonals added - * for triangulation can later be identified by having #NO_INDEX for original. - */ -static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena) -{ - /* Similar to loop body in BM_mesh_calc_tesselation. */ - int flen = f->size(); - BLI_assert(flen > 4); - if (!f->plane_populated()) { - f->populate_plane(false); - } - /* Project along negative face normal so (x,y) can be used in 2d. */ - const double3 &poly_normal = f->plane->norm; - float no[3] = {float(poly_normal[0]), float(poly_normal[1]), float(poly_normal[2])}; - normalize_v3(no); - float axis_mat[3][3]; - float(*projverts)[2]; - unsigned int(*tris)[3]; - const int totfilltri = flen - 2; - /* Prepare projected vertices and array to receive triangles in tesselation. */ - tris = static_cast<unsigned int(*)[3]>(MEM_malloc_arrayN(totfilltri, sizeof(*tris), __func__)); - projverts = static_cast<float(*)[2]>(MEM_malloc_arrayN(flen, sizeof(*projverts), __func__)); - axis_dominant_v3_to_m3_negate(axis_mat, no); - for (int j = 0; j < flen; ++j) { - const double3 &dco = (*f)[j]->co; - float co[3] = {float(dco[0]), float(dco[1]), float(dco[2])}; - mul_v2_m3v3(projverts[j], axis_mat, co); - } - BLI_polyfill_calc(projverts, flen, 1, tris); - /* Put tesselation triangles into Face form. Record original edges where they exist. */ - Array<Face *> ans(totfilltri); - for (int t = 0; t < totfilltri; ++t) { - unsigned int *tri = tris[t]; - int eo[3]; - const Vert *v[3]; - for (int k = 0; k < 3; k++) { - BLI_assert(tri[k] < flen); - v[k] = (*f)[tri[k]]; - /* If tri edge goes between two successive indices in - * the original face, then it is an original edge. */ - if ((tri[k] + 1) % flen == tri[(k + 1) % 3]) { - eo[k] = f->edge_orig[tri[k]]; - } - else { - eo[k] = NO_INDEX; - } - ans[t] = arena->add_face( - {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false}); - } - } - MEM_freeN(tris); - MEM_freeN(projverts); - - return ans; -} -# else -/** - * Which CDT output edge index is for an edge between output verts - * v1 and v2 (in either order)? - * \return -1 if none. - */ -static int find_cdt_edge(const CDT_result<mpq_class> &cdt_out, int v1, int v2) -{ - for (int e : cdt_out.edge.index_range()) { - const std::pair<int, int> &edge = cdt_out.edge[e]; - if ((edge.first == v1 && edge.second == v2) || (edge.first == v2 && edge.second == v1)) { - return e; - } - } - return -1; -} - -/** - * Tessellate face f into triangles and return an array of `const Face *` - * giving that triangulation. - * Care is taken so that the original edge index associated with - * each edge in the output triangles either matches the original edge - * for the (identical) edge of f, or else is -1. So diagonals added - * for triangulation can later be identified by having #NO_INDEX for original. - */ -static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena) -{ - int flen = f->size(); - CDT_input<mpq_class> cdt_in; - cdt_in.vert = Array<mpq2>(flen); - cdt_in.face = Array<Vector<int>>(1); - cdt_in.face[0].reserve(flen); - for (int i : f->index_range()) { - cdt_in.face[0].append(i); - } - /* Project poly along dominant axis of normal to get 2d coords. */ - if (!f->plane_populated()) { - f->populate_plane(false); - } - const double3 &poly_normal = f->plane->norm; - int axis = double3::dominant_axis(poly_normal); - /* If project down y axis as opposed to x or z, the orientation - * of the polygon will be reversed. - * Yet another reversal happens if the poly normal in the dominant - * direction is opposite that of the positive dominant axis. */ - bool rev1 = (axis == 1); - bool rev2 = poly_normal[axis] < 0; - bool rev = rev1 ^ rev2; - for (int i = 0; i < flen; ++i) { - int ii = rev ? flen - i - 1 : i; - mpq2 &p2d = cdt_in.vert[ii]; - int k = 0; - for (int j = 0; j < 3; ++j) { - if (j != axis) { - p2d[k++] = (*f)[ii]->co_exact[j]; - } - } - } - CDT_result<mpq_class> cdt_out = delaunay_2d_calc(cdt_in, CDT_INSIDE); - int n_tris = cdt_out.face.size(); - Array<Face *> ans(n_tris); - for (int t = 0; t < n_tris; ++t) { - int i_v_out[3]; - const Vert *v[3]; - int eo[3]; - for (int i = 0; i < 3; ++i) { - i_v_out[i] = cdt_out.face[t][i]; - v[i] = (*f)[cdt_out.vert_orig[i_v_out[i]][0]]; - } - for (int i = 0; i < 3; ++i) { - int e_out = find_cdt_edge(cdt_out, i_v_out[i], i_v_out[(i + 1) % 3]); - BLI_assert(e_out != -1); - eo[i] = NO_INDEX; - for (int orig : cdt_out.edge_orig[e_out]) { - if (orig != NO_INDEX) { - eo[i] = orig; - break; - } - } - } - if (rev) { - ans[t] = arena->add_face( - {v[0], v[2], v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {false, false, false}); - } - else { - ans[t] = arena->add_face( - {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false}); - } - } + ans.set_faces(out_faces); return ans; } -# endif - -/** - * Return an #IMesh that is a triangulation of a mesh with general - * polygonal faces, #IMesh. - * Added diagonals will be distinguishable by having edge original - * indices of #NO_INDEX. - */ -static IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena) -{ - Vector<Face *> face_tris; - constexpr int estimated_tris_per_face = 3; - face_tris.reserve(estimated_tris_per_face * imesh.face_size()); - for (Face *f : imesh.faces()) { - /* Tessellate face f, following plan similar to #BM_face_calc_tesselation. */ - int flen = f->size(); - if (flen == 3) { - face_tris.append(f); - } - else if (flen == 4) { - const Vert *v0 = (*f)[0]; - const Vert *v1 = (*f)[1]; - const Vert *v2 = (*f)[2]; - const Vert *v3 = (*f)[3]; - int eo_01 = f->edge_orig[0]; - int eo_12 = f->edge_orig[1]; - int eo_23 = f->edge_orig[2]; - int eo_30 = f->edge_orig[3]; - Face *f0 = arena->add_face({v0, v1, v2}, f->orig, {eo_01, eo_12, -1}, {false, false, false}); - Face *f1 = arena->add_face({v0, v2, v3}, f->orig, {-1, eo_23, eo_30}, {false, false, false}); - face_tris.append(f0); - face_tris.append(f1); - } - else { - Array<Face *> tris = triangulate_poly(f, arena); - for (Face *tri : tris) { - face_tris.append(tri); - } - } - } - return IMesh(face_tris); -} - /** * If \a tri1 and \a tri2 have a common edge (in opposite orientation), * return the indices into \a tri1 and \a tri2 where that common edge starts. Else return (-1,-1). diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index ce3a5b55f98..636209883c3 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -39,6 +39,7 @@ # include "BLI_math_mpq.hh" # include "BLI_mpq2.hh" # include "BLI_mpq3.hh" +# include "BLI_polyfill_2d.h" # include "BLI_set.hh" # include "BLI_span.hh" # include "BLI_task.h" @@ -1576,6 +1577,9 @@ struct CDT_data { Vector<bool> is_reversed; /** Result of running CDT on input with (vert, edge, face). */ CDT_result<mpq_class> cdt_out; + /** To speed up get_cdt_edge_orig, sometimes populate this map from vertex pair to output edge. + */ + Map<std::pair<int, int>, int> verts_to_edge; int proj_axis; }; @@ -1734,6 +1738,30 @@ static CDT_data prepare_cdt_input_for_cluster(const IMesh &tm, return ans; } +/* Return a copy of the argument with the integers ordered in ascending order. */ +static inline std::pair<int, int> sorted_int_pair(std::pair<int, int> pair) +{ + if (pair.first <= pair.second) { + return pair; + } + return std::pair<int, int>(pair.second, pair.first); +} + +/** + * Build cd.verts_to_edge to map from a pair of cdt output indices to an index in cd.cdt_out.edge. + * Order the vertex indices so that the smaller one is first in the pair. + */ +static void populate_cdt_edge_map(Map<std::pair<int, int>, int> &verts_to_edge, + const CDT_result<mpq_class> &cdt_out) +{ + verts_to_edge.reserve(cdt_out.edge.size()); + for (int e : cdt_out.edge.index_range()) { + std::pair<int, int> vpair = sorted_int_pair(cdt_out.edge[e]); + /* There should be only one edge for each vertex pair. */ + verts_to_edge.add(vpair, e); + } +} + /** * Fills in cd.cdt_out with result of doing the cdt calculation on (vert, edge, face). */ @@ -1766,6 +1794,10 @@ static void do_cdt(CDT_data &cd) } cdt_in.epsilon = 0; /* TODO: needs attention for non-exact T. */ cd.cdt_out = blender::meshintersect::delaunay_2d_calc(cdt_in, CDT_INSIDE); + constexpr int make_edge_map_threshold = 15; + if (cd.cdt_out.edge.size() >= make_edge_map_threshold) { + populate_cdt_edge_map(cd.verts_to_edge, cd.cdt_out); + } if (dbg_level > 0) { std::cout << "\nCDT result\nVerts:\n"; for (int i : cd.cdt_out.vert.index_range()) { @@ -1802,39 +1834,52 @@ static int get_cdt_edge_orig( { int foff = cd.cdt_out.face_edge_offset; *r_is_intersect = false; - for (int e : cd.cdt_out.edge.index_range()) { - std::pair<int, int> edge = cd.cdt_out.edge[e]; - if ((edge.first == i0 && edge.second == i1) || (edge.first == i1 && edge.second == i0)) { - /* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */ - /* TODO: if edge has origs from more than on part of the nary input, - * then want to set *r_is_intersect to true. */ - for (int orig_index : cd.cdt_out.edge_orig[e]) { - /* orig_index encodes the triangle and pos within the triangle of the input edge. */ - if (orig_index >= foff) { - int in_face_index = (orig_index / foff) - 1; - int pos = orig_index % foff; - /* We need to retrieve the edge orig field from the Face used to populate the - * in_face_index'th face of the CDT, at the pos'th position of the face. */ - int in_tm_face_index = cd.input_face[in_face_index]; - BLI_assert(in_tm_face_index < in_tm.face_size()); - const Face *facep = in_tm.face(in_tm_face_index); - BLI_assert(pos < facep->size()); - bool is_rev = cd.is_reversed[in_face_index]; - int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos]; - if (eorig != NO_INDEX) { - return eorig; - } - } - else { - /* This edge came from an edge input to the CDT problem, - * so it is an intersect edge. */ - *r_is_intersect = true; - /* TODO: maybe there is an orig index: - * This happens if an input edge was formed by an input face having - * an edge that is co-planar with the cluster, while the face as a whole is not. */ - return NO_INDEX; - } + int e = NO_INDEX; + if (cd.verts_to_edge.size() > 0) { + /* Use the populated map to find the edge, if any, between vertices i0 and i1. */ + std::pair<int, int> vpair = sorted_int_pair(std::pair<int, int>(i0, i1)); + e = cd.verts_to_edge.lookup_default(vpair, NO_INDEX); + } + else { + for (int ee : cd.cdt_out.edge.index_range()) { + std::pair<int, int> edge = cd.cdt_out.edge[ee]; + if ((edge.first == i0 && edge.second == i1) || (edge.first == i1 && edge.second == i0)) { + e = ee; + break; } + } + } + if (e == NO_INDEX) { + return NO_INDEX; + } + + /* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */ + /* TODO: if edge has origs from more than on part of the nary input, + * then want to set *r_is_intersect to true. */ + for (int orig_index : cd.cdt_out.edge_orig[e]) { + /* orig_index encodes the triangle and pos within the triangle of the input edge. */ + if (orig_index >= foff) { + int in_face_index = (orig_index / foff) - 1; + int pos = orig_index % foff; + /* We need to retrieve the edge orig field from the Face used to populate the + * in_face_index'th face of the CDT, at the pos'th position of the face. */ + int in_tm_face_index = cd.input_face[in_face_index]; + BLI_assert(in_tm_face_index < in_tm.face_size()); + const Face *facep = in_tm.face(in_tm_face_index); + BLI_assert(pos < facep->size()); + bool is_rev = cd.is_reversed[in_face_index]; + int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos]; + if (eorig != NO_INDEX) { + return eorig; + } + } + else { + /* This edge came from an edge input to the CDT problem, + * so it is an intersect edge. */ + *r_is_intersect = true; + /* TODO: maybe there is an orig index: + * This happens if an input edge was formed by an input face having + * an edge that is co-planar with the cluster, while the face as a whole is not. */ return NO_INDEX; } } @@ -1842,6 +1887,256 @@ static int get_cdt_edge_orig( } /** + * Make a Face from the CDT output triangle cdt_out_t, which has corresponding input triangle + * cdt_in_t. The triangle indices that are in cd.input_face are from IMesh tm. + * Return a pointer to the newly constructed Face. + */ +static Face *cdt_tri_as_imesh_face( + int cdt_out_t, int cdt_in_t, const CDT_data &cd, const IMesh &tm, IMeshArena *arena) +{ + const CDT_result<mpq_class> &cdt_out = cd.cdt_out; + int t_orig = tm.face(cd.input_face[cdt_in_t])->orig; + BLI_assert(cdt_out.face[cdt_out_t].size() == 3); + int i0 = cdt_out.face[cdt_out_t][0]; + int i1 = cdt_out.face[cdt_out_t][1]; + int i2 = cdt_out.face[cdt_out_t][2]; + mpq3 v0co = unproject_cdt_vert(cd, cdt_out.vert[i0]); + mpq3 v1co = unproject_cdt_vert(cd, cdt_out.vert[i1]); + mpq3 v2co = unproject_cdt_vert(cd, cdt_out.vert[i2]); + /* No need to provide an original index: if coord matches + * an original one, then it will already be in the arena + * with the correct orig field. */ + const Vert *v0 = arena->add_or_find_vert(v0co, NO_INDEX); + const Vert *v1 = arena->add_or_find_vert(v1co, NO_INDEX); + const Vert *v2 = arena->add_or_find_vert(v2co, NO_INDEX); + Face *facep; + bool is_isect0; + bool is_isect1; + bool is_isect2; + if (cd.is_reversed[cdt_in_t]) { + int oe0 = get_cdt_edge_orig(i0, i2, cd, tm, &is_isect0); + int oe1 = get_cdt_edge_orig(i2, i1, cd, tm, &is_isect1); + int oe2 = get_cdt_edge_orig(i1, i0, cd, tm, &is_isect2); + facep = arena->add_face( + {v0, v2, v1}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2}); + } + else { + int oe0 = get_cdt_edge_orig(i0, i1, cd, tm, &is_isect0); + int oe1 = get_cdt_edge_orig(i1, i2, cd, tm, &is_isect1); + int oe2 = get_cdt_edge_orig(i2, i0, cd, tm, &is_isect2); + facep = arena->add_face( + {v0, v1, v2}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2}); + } + facep->populate_plane(false); + return facep; +} + +/** + * Tessellate face f into triangles and return an array of `const Face *` + * giving that triangulation. Intended to be used when f has > 4 vertices. + * Care is taken so that the original edge index associated with + * each edge in the output triangles either matches the original edge + * for the (identical) edge of f, or else is -1. So diagonals added + * for triangulation can later be identified by having #NO_INDEX for original. + * + * This code uses Blenlib's BLI_polyfill_calc to do triangulation, and is therefore quite fast. + * Unfortunately, it can product degenerate triangles that mesh_intersect will remove, leaving + * the mesh non-PWN. + */ +static Array<Face *> polyfill_triangulate_poly(Face *f, IMeshArena *arena) +{ + /* Similar to loop body in BM_mesh_calc_tesselation. */ + int flen = f->size(); + BLI_assert(flen > 4); + if (!f->plane_populated()) { + f->populate_plane(false); + } + /* Project along negative face normal so (x,y) can be used in 2d. */ + const double3 &poly_normal = f->plane->norm; + float no[3] = {float(poly_normal[0]), float(poly_normal[1]), float(poly_normal[2])}; + normalize_v3(no); + float axis_mat[3][3]; + float(*projverts)[2]; + unsigned int(*tris)[3]; + const int totfilltri = flen - 2; + /* Prepare projected vertices and array to receive triangles in tesselation. */ + tris = static_cast<unsigned int(*)[3]>(MEM_malloc_arrayN(totfilltri, sizeof(*tris), __func__)); + projverts = static_cast<float(*)[2]>(MEM_malloc_arrayN(flen, sizeof(*projverts), __func__)); + axis_dominant_v3_to_m3_negate(axis_mat, no); + for (int j = 0; j < flen; ++j) { + const double3 &dco = (*f)[j]->co; + float co[3] = {float(dco[0]), float(dco[1]), float(dco[2])}; + mul_v2_m3v3(projverts[j], axis_mat, co); + } + BLI_polyfill_calc(projverts, flen, 1, tris); + /* Put tesselation triangles into Face form. Record original edges where they exist. */ + Array<Face *> ans(totfilltri); + for (int t = 0; t < totfilltri; ++t) { + unsigned int *tri = tris[t]; + int eo[3]; + const Vert *v[3]; + for (int k = 0; k < 3; k++) { + BLI_assert(tri[k] < flen); + v[k] = (*f)[tri[k]]; + /* If tri edge goes between two successive indices in + * the original face, then it is an original edge. */ + if ((tri[k] + 1) % flen == tri[(k + 1) % 3]) { + eo[k] = f->edge_orig[tri[k]]; + } + else { + eo[k] = NO_INDEX; + } + ans[t] = arena->add_face( + {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false}); + } + } + + MEM_freeN(tris); + MEM_freeN(projverts); + + return ans; +} + +/** + * Tessellate face f into triangles and return an array of `const Face *` + * giving that triangulation. + * Care is taken so that the original edge index associated with + * each edge in the output triangles either matches the original edge + * for the (identical) edge of f, or else is -1. So diagonals added + * for triangulation can later be identified by having #NO_INDEX for original. + * + * The method used is to use the CDT triangulation. Usually that triangulation + * will only use the existing vertices. However, if the face self-intersects + * then the CDT triangulation will include the intersection points. + * If this happens, we use the polyfill triangulator instead. We don't + * use the polyfill triangulator by default because it can create degenerate + * triangles (which we can handle but they'll create non-manifold meshes). + */ +static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena) +{ + int flen = f->size(); + CDT_input<mpq_class> cdt_in; + cdt_in.vert = Array<mpq2>(flen); + cdt_in.face = Array<Vector<int>>(1); + cdt_in.face[0].reserve(flen); + for (int i : f->index_range()) { + cdt_in.face[0].append(i); + } + /* Project poly along dominant axis of normal to get 2d coords. */ + if (!f->plane_populated()) { + f->populate_plane(false); + } + const double3 &poly_normal = f->plane->norm; + int axis = double3::dominant_axis(poly_normal); + /* If project down y axis as opposed to x or z, the orientation + * of the polygon will be reversed. + * Yet another reversal happens if the poly normal in the dominant + * direction is opposite that of the positive dominant axis. */ + bool rev1 = (axis == 1); + bool rev2 = poly_normal[axis] < 0; + bool rev = rev1 ^ rev2; + for (int i = 0; i < flen; ++i) { + int ii = rev ? flen - i - 1 : i; + mpq2 &p2d = cdt_in.vert[ii]; + int k = 0; + for (int j = 0; j < 3; ++j) { + if (j != axis) { + p2d[k++] = (*f)[ii]->co_exact[j]; + } + } + } + CDT_result<mpq_class> cdt_out = delaunay_2d_calc(cdt_in, CDT_INSIDE); + int n_tris = cdt_out.face.size(); + Array<Face *> ans(n_tris); + for (int t = 0; t < n_tris; ++t) { + int i_v_out[3]; + const Vert *v[3]; + int eo[3]; + bool needs_steiner = false; + for (int i = 0; i < 3; ++i) { + i_v_out[i] = cdt_out.face[t][i]; + if (cdt_out.vert_orig[i_v_out[i]].size() == 0) { + needs_steiner = true; + break; + } + v[i] = (*f)[cdt_out.vert_orig[i_v_out[i]][0]]; + } + if (needs_steiner) { + /* Fall back on the polyfill triangulator. */ + return polyfill_triangulate_poly(f, arena); + } + Map<std::pair<int, int>, int> verts_to_edge; + populate_cdt_edge_map(verts_to_edge, cdt_out); + int foff = cdt_out.face_edge_offset; + for (int i = 0; i < 3; ++i) { + std::pair<int, int> vpair(i_v_out[i], i_v_out[(i + 1) % 3]); + std::pair<int, int> vpair_canon = sorted_int_pair(vpair); + int e_out = verts_to_edge.lookup_default(vpair_canon, NO_INDEX); + BLI_assert(e_out != NO_INDEX); + eo[i] = NO_INDEX; + for (int orig : cdt_out.edge_orig[e_out]) { + if (orig >= foff) { + int pos = orig % foff; + BLI_assert(pos < f->size()); + eo[i] = f->edge_orig[pos]; + break; + } + } + } + if (rev) { + ans[t] = arena->add_face( + {v[0], v[2], v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {false, false, false}); + } + else { + ans[t] = arena->add_face( + {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false}); + } + } + return ans; +} + +/** + * Return an #IMesh that is a triangulation of a mesh with general + * polygonal faces, #IMesh. + * Added diagonals will be distinguishable by having edge original + * indices of #NO_INDEX. + */ +IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena) +{ + Vector<Face *> face_tris; + constexpr int estimated_tris_per_face = 3; + face_tris.reserve(estimated_tris_per_face * imesh.face_size()); + for (Face *f : imesh.faces()) { + /* Tessellate face f, following plan similar to #BM_face_calc_tesselation. */ + int flen = f->size(); + if (flen == 3) { + face_tris.append(f); + } + else if (flen == 4) { + const Vert *v0 = (*f)[0]; + const Vert *v1 = (*f)[1]; + const Vert *v2 = (*f)[2]; + const Vert *v3 = (*f)[3]; + int eo_01 = f->edge_orig[0]; + int eo_12 = f->edge_orig[1]; + int eo_23 = f->edge_orig[2]; + int eo_30 = f->edge_orig[3]; + Face *f0 = arena->add_face({v0, v1, v2}, f->orig, {eo_01, eo_12, -1}, {false, false, false}); + Face *f1 = arena->add_face({v0, v2, v3}, f->orig, {-1, eo_23, eo_30}, {false, false, false}); + face_tris.append(f0); + face_tris.append(f1); + } + else { + Array<Face *> tris = triangulate_poly(f, arena); + for (Face *tri : tris) { + face_tris.append(tri); + } + } + } + return IMesh(face_tris); +} + +/** * Using the result of CDT in cd.cdt_out, extract an #IMesh representing the subdivision * of input triangle t, which should be an element of cd.input_face. */ @@ -1862,55 +2157,17 @@ static IMesh extract_subdivided_tri(const CDT_data &cd, BLI_assert(false); return IMesh(); } - int t_orig = in_tm.face(t)->orig; constexpr int inline_buf_size = 20; Vector<Face *, inline_buf_size> faces; for (int f : cdt_out.face.index_range()) { if (cdt_out.face_orig[f].contains(t_in_cdt)) { - BLI_assert(cdt_out.face[f].size() == 3); - int i0 = cdt_out.face[f][0]; - int i1 = cdt_out.face[f][1]; - int i2 = cdt_out.face[f][2]; - mpq3 v0co = unproject_cdt_vert(cd, cdt_out.vert[i0]); - mpq3 v1co = unproject_cdt_vert(cd, cdt_out.vert[i1]); - mpq3 v2co = unproject_cdt_vert(cd, cdt_out.vert[i2]); - /* No need to provide an original index: if coord matches - * an original one, then it will already be in the arena - * with the correct orig field. */ - const Vert *v0 = arena->add_or_find_vert(v0co, NO_INDEX); - const Vert *v1 = arena->add_or_find_vert(v1co, NO_INDEX); - const Vert *v2 = arena->add_or_find_vert(v2co, NO_INDEX); - Face *facep; - bool is_isect0; - bool is_isect1; - bool is_isect2; - if (cd.is_reversed[t_in_cdt]) { - int oe0 = get_cdt_edge_orig(i0, i2, cd, in_tm, &is_isect0); - int oe1 = get_cdt_edge_orig(i2, i1, cd, in_tm, &is_isect1); - int oe2 = get_cdt_edge_orig(i1, i0, cd, in_tm, &is_isect2); - facep = arena->add_face( - {v0, v2, v1}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2}); - } - else { - int oe0 = get_cdt_edge_orig(i0, i1, cd, in_tm, &is_isect0); - int oe1 = get_cdt_edge_orig(i1, i2, cd, in_tm, &is_isect1); - int oe2 = get_cdt_edge_orig(i2, i0, cd, in_tm, &is_isect2); - facep = arena->add_face( - {v0, v1, v2}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2}); - } - facep->populate_plane(false); + Face *facep = cdt_tri_as_imesh_face(f, t_in_cdt, cd, in_tm, arena); faces.append(facep); } } return IMesh(faces); } -static IMesh extract_single_tri(const IMesh &tm, int t) -{ - Face *f = tm.face(t); - return IMesh({f}); -} - static bool bvhtreeverlap_cmp(const BVHTreeOverlap &a, const BVHTreeOverlap &b) { if (a.indexA < b.indexA) { @@ -2126,7 +2383,7 @@ static void calc_overlap_itts(Map<std::pair<int, int>, ITT_value> &itt_map, } /** - * Data needed for parallelization of calc_subdivided_tris. + * Data needed for parallelization of calc_subdivided_non_cluster_tris. */ struct OverlapTriRange { int tri_index; @@ -2203,14 +2460,13 @@ static void calc_subdivided_tri_range_func(void *__restrict userdata, * r_tri_subdivided with the result of intersecting it with * all the other triangles in the mesh, if it intersects any others. * But don't do this for triangles that are part of a cluster. - * Also, do nothing here if the answer is just the triangle itself. */ -static void calc_subdivided_tris(Array<IMesh> &r_tri_subdivided, - const IMesh &tm, - const Map<std::pair<int, int>, ITT_value> &itt_map, - const CoplanarClusterInfo &clinfo, - const TriOverlaps &ov, - IMeshArena *arena) +static void calc_subdivided_non_cluster_tris(Array<IMesh> &r_tri_subdivided, + const IMesh &tm, + const Map<std::pair<int, int>, ITT_value> &itt_map, + const CoplanarClusterInfo &clinfo, + const TriOverlaps &ov, + IMeshArena *arena) { const int dbg_level = 0; if (dbg_level > 0) { @@ -2250,6 +2506,54 @@ static void calc_subdivided_tris(Array<IMesh> &r_tri_subdivided, settings.use_threading = intersect_use_threading; BLI_task_parallel_range( 0, overlap_tri_range_tot, &data, calc_subdivided_tri_range_func, &settings); + /* Now have to put in the triangles that are the same as the input ones, and not in clusters. + */ + for (int t : tm.face_index_range()) { + if (r_tri_subdivided[t].face_size() == 0 && clinfo.tri_cluster(t) == NO_INDEX) { + r_tri_subdivided[t] = IMesh({tm.face(t)}); + } + } +} + +/** + * For each cluster in clinfo, extract the triangles from the cluster + * that correspond to each original triangle t that is part of the cluster, + * and put the resulting triangles into an IMesh in tri_subdivided[t]. + * We have already done the CDT for the triangles in the cluster, whose + * result is in cluster_subdivided[c] for each cluster c. + */ +static void calc_cluster_tris(Array<IMesh> &tri_subdivided, + const IMesh &tm, + const CoplanarClusterInfo &clinfo, + const Array<CDT_data> &cluster_subdivided, + IMeshArena *arena) +{ + for (int c : clinfo.index_range()) { + const CoplanarCluster &cl = clinfo.cluster(c); + const CDT_data &cd = cluster_subdivided[c]; + /* Each triangle in cluster c should be an input triangle in cd.input_faces. + * (See prepare_cdt_input_for_cluster.) + * So accumulate a Vector of Face* for each input face by going through the + * output faces and making a Face for each input face that it is part of. + * (The Boolean algorithm wants duplicates if a given output triangle is part + * of more than one input triangle.) + */ + int n_cluster_tris = cl.tot_tri(); + const CDT_result<mpq_class> &cdt_out = cd.cdt_out; + BLI_assert(cd.input_face.size() == n_cluster_tris); + Array<Vector<Face *>> face_vec(n_cluster_tris); + for (int cdt_out_t : cdt_out.face.index_range()) { + for (int cdt_in_t : cdt_out.face_orig[cdt_out_t]) { + Face *f = cdt_tri_as_imesh_face(cdt_out_t, cdt_in_t, cd, tm, arena); + face_vec[cdt_in_t].append(f); + } + } + for (int cdt_in_t : cd.input_face.index_range()) { + int tm_t = cd.input_face[cdt_in_t]; + BLI_assert(tri_subdivided[tm_t].face_size() == 0); + tri_subdivided[tm_t] = IMesh(face_vec[cdt_in_t]); + } + } } static CDT_data calc_cluster_subdivided(const CoplanarClusterInfo &clinfo, @@ -2622,10 +2926,11 @@ IMesh trimesh_nary_intersect(const IMesh &tm_in, doperfmax(2, tri_ov.overlap().size()); # endif Array<IMesh> tri_subdivided(tm_clean->face_size()); - calc_subdivided_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena); + calc_subdivided_non_cluster_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena); # ifdef PERFDEBUG double subdivided_tris_time = PIL_check_seconds_timer(); - std::cout << "subdivided tris found, time = " << subdivided_tris_time - itt_time << "\n"; + std::cout << "subdivided non-cluster tris found, time = " << subdivided_tris_time - itt_time + << "\n"; # endif Array<CDT_data> cluster_subdivided(clinfo.tot_cluster()); for (int c : clinfo.index_range()) { @@ -2636,19 +2941,11 @@ IMesh trimesh_nary_intersect(const IMesh &tm_in, std::cout << "subdivided clusters found, time = " << cluster_subdivide_time - subdivided_tris_time << "\n"; # endif - for (int t : tm_clean->face_index_range()) { - int c = clinfo.tri_cluster(t); - if (c != NO_INDEX) { - BLI_assert(tri_subdivided[t].face_size() == 0); - tri_subdivided[t] = extract_subdivided_tri(cluster_subdivided[c], *tm_clean, t, arena); - } - else if (tri_subdivided[t].face_size() == 0) { - tri_subdivided[t] = extract_single_tri(*tm_clean, t); - } - } + calc_cluster_tris(tri_subdivided, *tm_clean, clinfo, cluster_subdivided, arena); # ifdef PERFDEBUG double extract_time = PIL_check_seconds_timer(); - std::cout << "triangles extracted, time = " << extract_time - cluster_subdivide_time << "\n"; + std::cout << "subdivided cluster tris found, time = " << extract_time - cluster_subdivide_time + << "\n"; # endif IMesh combined = union_tri_subdivides(tri_subdivided); if (dbg_level > 1) { diff --git a/source/blender/blenlib/intern/storage.c b/source/blender/blenlib/intern/storage.c index 287334a34ee..5f823396ed9 100644 --- a/source/blender/blenlib/intern/storage.c +++ b/source/blender/blenlib/intern/storage.c @@ -266,7 +266,8 @@ eFileAttributes BLI_file_attributes(const char *path) if (attr & FILE_ATTRIBUTE_SPARSE_FILE) { ret |= FILE_ATTR_SPARSE_FILE; } - if (attr & FILE_ATTRIBUTE_OFFLINE) { + if (attr & FILE_ATTRIBUTE_OFFLINE || attr & FILE_ATTRIBUTE_RECALL_ON_OPEN || + attr & FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS) { ret |= FILE_ATTR_OFFLINE; } if (attr & FILE_ATTRIBUTE_REPARSE_POINT) { @@ -299,12 +300,16 @@ bool BLI_file_alias_target(const char *filepath, return false; } - IShellLinkW *Shortcut = NULL; - bool success = false; - CoInitializeEx(NULL, COINIT_MULTITHREADED); + HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); + if (FAILED(hr)) { + return false; + } - HRESULT hr = CoCreateInstance( + IShellLinkW *Shortcut = NULL; + hr = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLinkW, (LPVOID *)&Shortcut); + + bool success = false; if (SUCCEEDED(hr)) { IPersistFile *PersistFile; hr = Shortcut->lpVtbl->QueryInterface(Shortcut, &IID_IPersistFile, (LPVOID *)&PersistFile); @@ -328,6 +333,7 @@ bool BLI_file_alias_target(const char *filepath, Shortcut->lpVtbl->Release(Shortcut); } + CoUninitialize(); return (success && r_targetpath[0]); # else UNUSED_VARS(r_targetpath, filepath); diff --git a/source/blender/blenlib/intern/uvproject.c b/source/blender/blenlib/intern/uvproject.c index 329c4d48fe8..093d08e643d 100644 --- a/source/blender/blenlib/intern/uvproject.c +++ b/source/blender/blenlib/intern/uvproject.c @@ -134,7 +134,7 @@ void BLI_uvproject_from_view(float target[2], /* 'rotmat' can be `obedit->obmat` when uv project is used. * 'winx' and 'winy' can be from `scene->r.xsch/ysch` */ -ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float (*rotmat)[4], float winx, float winy) +ProjCameraInfo *BLI_uvproject_camera_info(Object *ob, float rotmat[4][4], float winx, float winy) { ProjCameraInfo uci; Camera *camera = ob->data; diff --git a/source/blender/blenlib/tests/BLI_color_test.cc b/source/blender/blenlib/tests/BLI_color_test.cc new file mode 100644 index 00000000000..14796e6bf71 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_color_test.cc @@ -0,0 +1,133 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_color.hh" + +namespace blender::tests { + +/** + * \name Conversions + * \{ */ + +TEST(color, ThemeByteToFloat) +{ + ColorTheme4b theme_byte(192, 128, 64, 128); + ColorTheme4f theme_float = theme_byte.to_4f(); + EXPECT_NEAR(0.75f, theme_float.r, 0.01f); + EXPECT_NEAR(0.5f, theme_float.g, 0.01f); + EXPECT_NEAR(0.25f, theme_float.b, 0.01f); + EXPECT_NEAR(0.5f, theme_float.a, 0.01f); +} + +TEST(color, SrgbStraightFloatToByte) +{ + ColorTheme4f theme_float(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme_byte = theme_float.to_4b(); + EXPECT_EQ(191, theme_byte.r); + EXPECT_EQ(128, theme_byte.g); + EXPECT_EQ(64, theme_byte.b); + EXPECT_EQ(128, theme_byte.a); +} + +TEST(color, SrgbStraightToSceneLinearPremultiplied) +{ + BLI_init_srgb_conversion(); + + ColorTheme4b theme(192, 128, 64, 128); + ColorSceneLinear4f<eAlpha::Premultiplied> linear = + BLI_color_convert_to_scene_linear(theme).premultiply_alpha(); + EXPECT_NEAR(0.26f, linear.r, 0.01f); + EXPECT_NEAR(0.11f, linear.g, 0.01f); + EXPECT_NEAR(0.02f, linear.b, 0.01f); + EXPECT_NEAR(0.5f, linear.a, 0.01f); +} + +TEST(color, SceneLinearStraightToPremultiplied) +{ + ColorSceneLinear4f<eAlpha::Straight> straight(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied = straight.premultiply_alpha(); + EXPECT_NEAR(0.37f, premultiplied.r, 0.01f); + EXPECT_NEAR(0.25f, premultiplied.g, 0.01f); + EXPECT_NEAR(0.12f, premultiplied.b, 0.01f); + EXPECT_NEAR(0.5f, premultiplied.a, 0.01f); +} + +TEST(color, SceneLinearPremultipliedToStraight) +{ + ColorSceneLinear4f<eAlpha::Premultiplied> premultiplied(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinear4f<eAlpha::Straight> straight = premultiplied.unpremultiply_alpha(); + EXPECT_NEAR(1.5f, straight.r, 0.01f); + EXPECT_NEAR(1.0f, straight.g, 0.01f); + EXPECT_NEAR(0.5f, straight.b, 0.01f); + EXPECT_NEAR(0.5f, straight.a, 0.01f); +} + +TEST(color, SceneLinearStraightSrgbFloat) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f<eAlpha::Straight> linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4f theme = BLI_color_convert_to_theme4f(linear); + EXPECT_NEAR(0.88f, theme.r, 0.01); + EXPECT_NEAR(0.73f, theme.g, 0.01); + EXPECT_NEAR(0.53f, theme.b, 0.01); + EXPECT_NEAR(0.5f, theme.a, 0.01); +} + +TEST(color, SceneLinearPremultipliedToSrgbFloat) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4f theme = BLI_color_convert_to_theme4f(linear.unpremultiply_alpha()); + + EXPECT_NEAR(1.19f, theme.r, 0.01); + EXPECT_NEAR(1.0f, theme.g, 0.01); + EXPECT_NEAR(0.74f, theme.b, 0.01); + EXPECT_NEAR(0.5f, theme.a, 0.01); +} + +TEST(color, SceneLinearStraightSrgbByte) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f<eAlpha::Straight> linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme = BLI_color_convert_to_theme4b(linear); + EXPECT_EQ(225, theme.r); + EXPECT_EQ(188, theme.g); + EXPECT_EQ(137, theme.b); + EXPECT_EQ(128, theme.a); +} + +TEST(color, SceneLinearPremultipliedToSrgbByte) +{ + BLI_init_srgb_conversion(); + ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorTheme4b theme = BLI_color_convert_to_theme4b(linear.unpremultiply_alpha()); + EXPECT_EQ(255, theme.r); + EXPECT_EQ(255, theme.g); + EXPECT_EQ(188, theme.b); + EXPECT_EQ(128, theme.a); +} + +TEST(color, SceneLinearByteEncoding) +{ + ColorSceneLinear4f<eAlpha::Premultiplied> linear(0.75f, 0.5f, 0.25f, 0.5f); + ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied> encoded = linear.encode(); + EXPECT_EQ(225, encoded.r); + EXPECT_EQ(188, encoded.g); + EXPECT_EQ(137, encoded.b); + EXPECT_EQ(128, encoded.a); +} + +TEST(color, SceneLinearByteDecoding) +{ + ColorSceneLinearByteEncoded4b<eAlpha::Premultiplied> encoded(225, 188, 137, 128); + ColorSceneLinear4f<eAlpha::Premultiplied> decoded = encoded.decode(); + EXPECT_NEAR(0.75f, decoded.r, 0.01f); + EXPECT_NEAR(0.5f, decoded.g, 0.01f); + EXPECT_NEAR(0.25f, decoded.b, 0.01f); + EXPECT_NEAR(0.5f, decoded.a, 0.01f); +} + +/* \} */ + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc index 977e5dba497..0e0145e592a 100644 --- a/source/blender/blenlib/tests/BLI_linear_allocator_test.cc +++ b/source/blender/blenlib/tests/BLI_linear_allocator_test.cc @@ -136,4 +136,17 @@ TEST(linear_allocator, ManyAllocations) } } +TEST(linear_allocator, ConstructArray) +{ + LinearAllocator<> allocator; + MutableSpan<std::string> strings = allocator.construct_array<std::string>(4, "hello"); + EXPECT_EQ(strings[0], "hello"); + EXPECT_EQ(strings[1], "hello"); + EXPECT_EQ(strings[2], "hello"); + EXPECT_EQ(strings[3], "hello"); + for (std::string &string : strings) { + string.~basic_string(); + } +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc index bb15f7f0d8d..679a10e9ce0 100644 --- a/source/blender/blenlib/tests/BLI_map_test.cc +++ b/source/blender/blenlib/tests/BLI_map_test.cc @@ -613,6 +613,46 @@ TEST(map, AddAsVariadic) EXPECT_EQ(map.lookup(2), "t"); } +TEST(map, RemoveDuringIteration) +{ + Map<int, int> map; + map.add(2, 1); + map.add(5, 2); + map.add(1, 2); + map.add(6, 0); + map.add(3, 3); + + EXPECT_EQ(map.size(), 5); + + using Iter = Map<int, int>::MutableItemIterator; + Iter begin = map.items().begin(); + Iter end = map.items().end(); + for (Iter iter = begin; iter != end; ++iter) { + Map<int, int>::MutableItem item = *iter; + if (item.value == 2) { + map.remove(iter); + } + } + + EXPECT_EQ(map.size(), 3); + EXPECT_EQ(map.lookup(2), 1); + EXPECT_EQ(map.lookup(6), 0); + EXPECT_EQ(map.lookup(3), 3); +} + +TEST(map, LookupKey) +{ + Map<std::string, int> map; + map.add("a", 0); + map.add("b", 1); + map.add("c", 2); + EXPECT_EQ(map.lookup_key("a"), "a"); + EXPECT_EQ(map.lookup_key_as("c"), "c"); + EXPECT_EQ(map.lookup_key_ptr_as("d"), nullptr); + EXPECT_EQ(map.lookup_key_ptr_as("b")->size(), 1); + EXPECT_EQ(map.lookup_key_ptr("a"), map.lookup_key_ptr_as("a")); +} + /** * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. */ diff --git a/source/blender/blenlib/tests/BLI_vector_set_test.cc b/source/blender/blenlib/tests/BLI_vector_set_test.cc index bbbe96f9b7e..c4016ca75e1 100644 --- a/source/blender/blenlib/tests/BLI_vector_set_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_set_test.cc @@ -232,4 +232,43 @@ TEST(vector_set, PopExceptions) EXPECT_EQ(set.size(), 4); } +TEST(vector_set, IndexOfOrAdd) +{ + VectorSet<int> set; + EXPECT_EQ(set.index_of_or_add(3), 0); + EXPECT_EQ(set.index_of_or_add(3), 0); + EXPECT_EQ(set.index_of_or_add(2), 1); + EXPECT_EQ(set.index_of_or_add(0), 2); + EXPECT_EQ(set.index_of_or_add(2), 1); + EXPECT_EQ(set.index_of_or_add(3), 0); + EXPECT_EQ(set.index_of_or_add(5), 3); + EXPECT_EQ(set.index_of_or_add(8), 4); + EXPECT_EQ(set.index_of_or_add(5), 3); +} + +TEST(vector_set, Clear) +{ + VectorSet<int> set = {4, 6, 2, 4}; + EXPECT_EQ(set.size(), 3); + set.clear(); + EXPECT_EQ(set.size(), 0); + set.add_multiple({4, 1, 6, 8, 3, 6, 9, 3}); + EXPECT_EQ(set.size(), 6); + set.clear(); + EXPECT_EQ(set.size(), 0); +} + +TEST(vector_set, LookupKey) +{ + VectorSet<std::string> set; + set.add("a"); + set.add("b"); + set.add("c"); + EXPECT_EQ(set.lookup_key("a"), "a"); + EXPECT_EQ(set.lookup_key_as("c"), "c"); + EXPECT_EQ(set.lookup_key_ptr_as("d"), nullptr); + EXPECT_EQ(set.lookup_key_ptr_as("b")->size(), 1); + EXPECT_EQ(set.lookup_key_ptr("a"), set.lookup_key_ptr_as("a")); +} + } // namespace blender::tests |