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