Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenlib')
-rw-r--r--source/blender/blenlib/BLI_color.hh302
-rw-r--r--source/blender/blenlib/BLI_compiler_attrs.h7
-rw-r--r--source/blender/blenlib/BLI_enumerable_thread_specific.hh73
-rw-r--r--source/blender/blenlib/BLI_fileops.h2
-rw-r--r--source/blender/blenlib/BLI_float3.hh7
-rw-r--r--source/blender/blenlib/BLI_float4x4.hh44
-rw-r--r--source/blender/blenlib/BLI_hash.hh22
-rw-r--r--source/blender/blenlib/BLI_linear_allocator.hh15
-rw-r--r--source/blender/blenlib/BLI_map.hh115
-rw-r--r--source/blender/blenlib/BLI_math_color.h5
-rw-r--r--source/blender/blenlib/BLI_math_geom.h10
-rw-r--r--source/blender/blenlib/BLI_math_matrix.h2
-rw-r--r--source/blender/blenlib/BLI_math_solvers.h2
-rw-r--r--source/blender/blenlib/BLI_math_vector.h8
-rw-r--r--source/blender/blenlib/BLI_mesh_intersect.hh3
-rw-r--r--source/blender/blenlib/BLI_span.hh10
-rw-r--r--source/blender/blenlib/BLI_vector.hh24
-rw-r--r--source/blender/blenlib/BLI_vector_set.hh77
-rw-r--r--source/blender/blenlib/BLI_virtual_array.hh85
-rw-r--r--source/blender/blenlib/CMakeLists.txt3
-rw-r--r--source/blender/blenlib/intern/BLI_color.cc55
-rw-r--r--source/blender/blenlib/intern/BLI_dial_2d.c2
-rw-r--r--source/blender/blenlib/intern/math_color.c72
-rw-r--r--source/blender/blenlib/intern/math_geom.c4
-rw-r--r--source/blender/blenlib/intern/math_vector.c2
-rw-r--r--source/blender/blenlib/intern/mesh_boolean.cc204
-rw-r--r--source/blender/blenlib/intern/mesh_intersect.cc481
-rw-r--r--source/blender/blenlib/intern/storage.c16
-rw-r--r--source/blender/blenlib/intern/uvproject.c2
-rw-r--r--source/blender/blenlib/tests/BLI_color_test.cc133
-rw-r--r--source/blender/blenlib/tests/BLI_linear_allocator_test.cc13
-rw-r--r--source/blender/blenlib/tests/BLI_map_test.cc40
-rw-r--r--source/blender/blenlib/tests/BLI_vector_set_test.cc39
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 &current_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 &current_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