1 #ifndef MTUTILS_HPP
2 #define MTUTILS_HPP
3
4 #include <atomic> // for std::atomic_flag and memory orders
5 #include <mutex> // for std::lock_guard
6 #include <functional> // for std::function
7 #include <utility> // for std::forward
8 #include <vector>
9 #include <algorithm>
10 #include <cmath>
11
12 #include "libslic3r.h"
13
14 namespace Slic3r {
15
16 /// Handy little spin mutex for the cached meshes.
17 /// Implements the "Lockable" concept
18 class SpinMutex
19 {
20 std::atomic_flag m_flg;
21 static const /*constexpr*/ auto MO_ACQ = std::memory_order_acquire;
22 static const /*constexpr*/ auto MO_REL = std::memory_order_release;
23
24 public:
SpinMutex()25 inline SpinMutex() { m_flg.clear(MO_REL); }
lock()26 inline void lock() { while (m_flg.test_and_set(MO_ACQ)) ; }
try_lock()27 inline bool try_lock() { return !m_flg.test_and_set(MO_ACQ); }
unlock()28 inline void unlock() { m_flg.clear(MO_REL); }
29 };
30
31 /// A wrapper class around arbitrary object that needs thread safe caching.
32 template<class T> class CachedObject
33 {
34 public:
35 // Method type which refreshes the object when it has been invalidated
36 using Setter = std::function<void(T &)>;
37
38 private:
39 T m_obj; // the object itself
40 bool m_valid; // invalidation flag
41 SpinMutex m_lck; // to make the caching thread safe
42
43 // the setter will be called just before the object's const value is
44 // about to be retrieved.
45 std::function<void(T &)> m_setter;
46
47 public:
48 // Forwarded constructor
49 template<class... Args>
CachedObject(Setter fn,Args &&...args)50 inline CachedObject(Setter fn, Args &&... args)
51 : m_obj(std::forward<Args>(args)...), m_valid(false), m_setter(fn)
52 {}
53
54 // invalidate the value of the object. The object will be refreshed at
55 // the next retrieval (Setter will be called). The data that is used in
56 // the setter function should be guarded as well during modification so
57 // the modification has to take place in fn.
invalidate(std::function<void ()> fn)58 inline void invalidate(std::function<void()> fn)
59 {
60 std::lock_guard<SpinMutex> lck(m_lck);
61 fn();
62 m_valid = false;
63 }
64
65 // Get the const object properly updated.
get()66 inline const T &get()
67 {
68 std::lock_guard<SpinMutex> lck(m_lck);
69 if (!m_valid) {
70 m_setter(m_obj);
71 m_valid = true;
72 }
73 return m_obj;
74 }
75 };
76
77 /// A very simple range concept implementation with iterator-like objects.
78 template<class It> class Range
79 {
80 It from, to;
81
82 public:
83 // The class is ready for range based for loops.
begin() const84 It begin() const { return from; }
end() const85 It end() const { return to; }
86
87 // The iterator type can be obtained this way.
88 using Type = It;
89
90 Range() = default;
Range(It && b,It && e)91 Range(It &&b, It &&e)
92 : from(std::forward<It>(b)), to(std::forward<It>(e))
93 {}
94
95 // Some useful container-like methods...
size() const96 inline size_t size() const { return end() - begin(); }
empty() const97 inline bool empty() const { return size() == 0; }
98 };
99
all_of(const C & container)100 template<class C> bool all_of(const C &container)
101 {
102 return std::all_of(container.begin(),
103 container.end(),
104 [](const typename C::value_type &v) {
105 return static_cast<bool>(v);
106 });
107 }
108
109 template<class T> struct remove_cvref
110 {
111 using type =
112 typename std::remove_cv<typename std::remove_reference<T>::type>::type;
113 };
114
115 template<class T> using remove_cvref_t = typename remove_cvref<T>::type;
116
117 /// Exactly like Matlab https://www.mathworks.com/help/matlab/ref/linspace.html
118 template<class T, class I, class = IntegerOnly<I>>
linspace_vector(const ArithmeticOnly<T> & start,const T & stop,const I & n)119 inline std::vector<T> linspace_vector(const ArithmeticOnly<T> &start,
120 const T &stop,
121 const I &n)
122 {
123 std::vector<T> vals(n, T());
124
125 T stride = (stop - start) / n;
126 size_t i = 0;
127 std::generate(vals.begin(), vals.end(), [&i, start, stride] {
128 return start + i++ * stride;
129 });
130
131 return vals;
132 }
133
134 template<size_t N, class T>
linspace_array(const T & start,const T & stop)135 inline std::array<ArithmeticOnly<T>, N> linspace_array(const T &start, const T &stop)
136 {
137 std::array<T, N> vals = {T()};
138
139 T stride = (stop - start) / N;
140 size_t i = 0;
141 std::generate(vals.begin(), vals.end(), [&i, start, stride] {
142 return start + i++ * stride;
143 });
144
145 return vals;
146 }
147
148 /// A set of equidistant values starting from 'start' (inclusive), ending
149 /// in the closest multiple of 'stride' less than or equal to 'end' and
150 /// leaving 'stride' space between each value.
151 /// Very similar to Matlab [start:stride:end] notation.
152 template<class T>
grid(const T & start,const T & stop,const T & stride)153 inline std::vector<ArithmeticOnly<T>> grid(const T &start,
154 const T &stop,
155 const T &stride)
156 {
157 std::vector<T> vals(size_t(std::ceil((stop - start) / stride)), T());
158
159 int i = 0;
160 std::generate(vals.begin(), vals.end(), [&i, start, stride] {
161 return start + i++ * stride;
162 });
163
164 return vals;
165 }
166
167 } // namespace Slic3r
168
169 #endif // MTUTILS_HPP
170