uniform.hh 8.79 KB
Newer Older
Philip Trettner's avatar
Philip Trettner committed
1
2
#pragma once

Philip Trettner's avatar
Philip Trettner committed
3
4
#include <typed-geometry/common/random.hh>
#include <typed-geometry/detail/scalar_traits.hh>
Philip Trettner's avatar
Philip Trettner committed
5

Philip Trettner's avatar
Philip Trettner committed
6
#include <typed-geometry/types/color.hh>
Philip Trettner's avatar
Philip Trettner committed
7
#include <typed-geometry/types/pos.hh>
8
#include <typed-geometry/types/range.hh>
Philip Trettner's avatar
Philip Trettner committed
9
#include <typed-geometry/types/scalar.hh>
10

Philip Trettner's avatar
Philip Trettner committed
11
#include <typed-geometry/types/objects/aabb.hh>
Philip Trettner's avatar
Philip Trettner committed
12
13
#include <typed-geometry/types/objects/ball.hh>
#include <typed-geometry/types/objects/box.hh>
Philip Trettner's avatar
Philip Trettner committed
14
15
#include <typed-geometry/types/objects/sphere.hh>
#include <typed-geometry/types/objects/triangle.hh>
16

17
#include "math.hh"
18
#include "minmax.hh"
19
#include "mix.hh"
Philip Trettner's avatar
Philip Trettner committed
20

21
22
#include <initializer_list>

Philip Trettner's avatar
Philip Trettner committed
23
24
/*
 * uniform(rng)       - fair coin flip
25
 * uniform(rng, a, b) - uniform between a..b (inclusive!)
Philip Trettner's avatar
Philip Trettner committed
26
 * uniform(rng, obj)  - uniform sample from obj
27
 *
28
29
30
31
 * Note:
 *  to "uniformly sample" vec3(0)..vec3(1) independent per component, use uniform(rng, tg::aabb3(0, 1))
 *  to "uniformly sample" vec3(0)..vec3(1) as a segment, use uniform(rng, tg::segment3({0,0,0}, {1,1,1}))
 *
32
 * uniform_vec(rng, ...) same as uniform but returns uniform(rng, ...) as a vector
Philip Trettner's avatar
Philip Trettner committed
33
34
 */

35
36
// TODO: uniform int/uint distribution might need some improvement but is currently faster than the stdlib versions

Philip Trettner's avatar
Philip Trettner committed
37
38
namespace tg
{
Philip Trettner's avatar
Philip Trettner committed
39
40
41
42
43
44
45
46
47
48
namespace detail
{
template <class T>
struct sampler
{
    static_assert(sizeof(T) >= 0, "sampling of this type not supported without parameters");
};
}

template <class T, class Rng>
49
TG_NODISCARD constexpr T uniform(Rng& rng)
Philip Trettner's avatar
Philip Trettner committed
50
{
Philip Trettner's avatar
Philip Trettner committed
51
    return detail::sampler<T>::uniform(rng);
Philip Trettner's avatar
Philip Trettner committed
52
53
54
}

template <class Rng>
55
TG_NODISCARD constexpr f32 uniform(Rng& rng, f32 a, f32 b)
Philip Trettner's avatar
Philip Trettner committed
56
57
58
59
{
    return mix(a, b, detail::uniform01<f32>(rng));
}
template <class Rng>
60
TG_NODISCARD constexpr f64 uniform(Rng& rng, f64 a, f64 b)
Philip Trettner's avatar
Philip Trettner committed
61
62
63
64
{
    return mix(a, b, detail::uniform01<f64>(rng));
}
template <class Rng>
65
TG_NODISCARD constexpr i32 uniform(Rng& rng, i32 a, i32 b_inc)
Philip Trettner's avatar
Philip Trettner committed
66
{
67
    i32 r = 0;
68
    auto fa = f32(a);
69
    auto fb = f32(b_inc) + 1;
70
71
72
    do
    {
        r = tg::ifloor(uniform(rng, fa, fb));
73
    } while (r > b_inc);
74
    return r;
Philip Trettner's avatar
Philip Trettner committed
75
76
}
template <class Rng>
77
TG_NODISCARD constexpr i64 uniform(Rng& rng, i64 a, i64 b_inc)
Philip Trettner's avatar
Philip Trettner committed
78
{
79
    i64 r = 0;
80
    auto fa = f64(a);
81
    auto fb = f64(b_inc) + 1;
82
83
84
    do
    {
        r = tg::ifloor(uniform(rng, fa, fb));
85
    } while (r > b_inc);
86
    return r;
Philip Trettner's avatar
Philip Trettner committed
87
88
}
template <class Rng>
89
TG_NODISCARD constexpr u32 uniform(Rng& rng, u32 a, u32 b_inc)
Philip Trettner's avatar
Philip Trettner committed
90
{
91
    u32 r = 0;
92
    auto fa = f32(a);
93
    auto fb = f32(b_inc) + 1;
94
95
96
    do
    {
        r = u32(tg::ifloor(uniform(rng, fa, fb)));
97
    } while (r > b_inc);
98
    return r;
Philip Trettner's avatar
Philip Trettner committed
99
100
}
template <class Rng>
101
TG_NODISCARD constexpr u64 uniform(Rng& rng, u64 a, u64 b_inc)
Philip Trettner's avatar
Philip Trettner committed
102
{
103
    u64 r = 0;
104
    auto fa = f64(a);
105
    auto fb = f64(b_inc) + 1;
106
107
    do
    {
108
        r = u64(tg::ifloor(uniform(rng, fa, fb)));
109
    } while (r > b_inc);
110
    return r;
Philip Trettner's avatar
Philip Trettner committed
111
112
}

113
template <class T, class Rng>
114
TG_NODISCARD constexpr angle_t<T> uniform(Rng& rng, angle_t<T> a, angle_t<T> b)
115
116
117
118
{
    return mix(a, b, detail::uniform01<T>(rng));
}

119
120
121
122
123
124
125
template <class Rng, class T>
TG_NODISCARD constexpr T uniform(Rng& rng, std::initializer_list<T> list)
{
    TG_CONTRACT(list.size() > 0 && "cannot pick from an empty list");
    return list.begin()[uniform(rng, u64(0), u64(list.size() - 1))];
}

126
127
128
129
template <class Rng, class Container>
TG_NODISCARD constexpr auto uniform(Rng& rng, Container const& c) -> decltype(c[c.size()])
{
    TG_CONTRACT(c.size() > 0 && "cannot pick from an empty container");
130
    return c[uniform(rng, u64(0), u64(c.size() - 1))];
131
132
}

133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
template <class Rng>
TG_NODISCARD constexpr int uniform(Rng& rng, range1 const& b)
{
    return uniform(rng, b.min, b.max - 1);
}

template <class Rng>
TG_NODISCARD constexpr comp<2, int> uniform(Rng& rng, range2 const& b)
{
    return {uniform(rng, b.min.comp0, b.max.comp0 - 1), //
            uniform(rng, b.min.comp1, b.max.comp1 - 1)};
}

template <class Rng>
TG_NODISCARD constexpr comp<3, int> uniform(Rng& rng, range3 const& b)
{
    return {uniform(rng, b.min.comp0, b.max.comp0 - 1), //
            uniform(rng, b.min.comp1, b.max.comp1 - 1), //
            uniform(rng, b.min.comp2, b.max.comp2 - 1)};
}

Philip Trettner's avatar
Philip Trettner committed
154
template <class ScalarT, class Rng>
155
TG_NODISCARD constexpr pos<1, ScalarT> uniform(Rng& rng, aabb<1, ScalarT> const& b)
Philip Trettner's avatar
Philip Trettner committed
156
{
157
    return {uniform(rng, b.min.x, b.max.x)};
Philip Trettner's avatar
Philip Trettner committed
158
159
}
template <class ScalarT, class Rng>
160
TG_NODISCARD constexpr pos<2, ScalarT> uniform(Rng& rng, aabb<2, ScalarT> const& b)
Philip Trettner's avatar
Philip Trettner committed
161
162
163
164
165
{
    return {uniform(rng, b.min.x, b.max.x), //
            uniform(rng, b.min.y, b.max.y)};
}
template <class ScalarT, class Rng>
166
TG_NODISCARD constexpr pos<3, ScalarT> uniform(Rng& rng, aabb<3, ScalarT> const& b)
Philip Trettner's avatar
Philip Trettner committed
167
168
169
170
171
172
{
    return {uniform(rng, b.min.x, b.max.x), //
            uniform(rng, b.min.y, b.max.y), //
            uniform(rng, b.min.z, b.max.z)};
}
template <class ScalarT, class Rng>
173
TG_NODISCARD constexpr pos<4, ScalarT> uniform(Rng& rng, aabb<4, ScalarT> const& b)
Philip Trettner's avatar
Philip Trettner committed
174
175
176
177
178
179
180
{
    return {uniform(rng, b.min.x, b.max.x), //
            uniform(rng, b.min.y, b.max.y), //
            uniform(rng, b.min.z, b.max.z), //
            uniform(rng, b.min.w, b.max.w)};
}

Philip Trettner's avatar
Philip Trettner committed
181
template <int D, class ScalarT, class Rng>
182
TG_NODISCARD constexpr pos<D, ScalarT> uniform(Rng& rng, box<D, ScalarT> const& b)
Philip Trettner's avatar
Philip Trettner committed
183
184
185
186
{
    return b.center + b.half_extents * uniform_vec(rng, aabb<D, ScalarT>::minus_one_to_one);
}

187
template <int D, class ScalarT, class Rng>
188
TG_NODISCARD constexpr pos<D, ScalarT> uniform(Rng& rng, sphere<D, ScalarT> const& s)
189
{
Philip Trettner's avatar
Philip Trettner committed
190
    auto ub = tg::aabb<D, ScalarT>::minus_one_to_one;
191
192
193
    while (true)
    {
        auto p = uniform_vec(rng, ub);
194
        auto l = length_sqr(p);
Philip Trettner's avatar
Philip Trettner committed
195
        if (l > ScalarT(0) && l <= ScalarT(1))
196
197
198
199
200
            return s.center + p * (s.radius / tg::sqrt(l));
    }
}

template <int D, class ScalarT, class Rng>
201
TG_NODISCARD constexpr pos<D, ScalarT> uniform(Rng& rng, ball<D, ScalarT> const& b)
202
{
Philip Trettner's avatar
Philip Trettner committed
203
    auto ub = tg::aabb<D, ScalarT>::minus_one_to_one;
204
205
206
    while (true)
    {
        auto p = uniform_vec(rng, ub);
207
        auto l = length_sqr(p);
Philip Trettner's avatar
Philip Trettner committed
208
        if (l <= ScalarT(1))
209
210
211
212
            return b.center + p * b.radius;
    }
}

213
template <int D, class ScalarT, class Rng, class = enable_if<is_floating_point<ScalarT>>>
214
215
216
[[deprecated("potentially misleading operation. use uniform_vec(rng, tg::aabb3(..)) or uniform_vec(rng, tg::segment3(..)) depending on your intended "
             "semantics")]] TG_NODISCARD constexpr vec<D, ScalarT>
uniform(Rng& rng, vec<D, ScalarT> const& a, vec<D, ScalarT> const& b)
Philip Trettner's avatar
Philip Trettner committed
217
218
219
{
    return mix(a, b, detail::uniform01<ScalarT>(rng));
}
220
template <int D, class ScalarT, class Rng, class = enable_if<is_floating_point<ScalarT>>>
221
222
223
[[deprecated("potentially misleading operation. use uniform(rng, tg::aabb3(..)) or uniform(rng, tg::segment3(..)) depending on your intended "
             "semantics")]] TG_NODISCARD constexpr pos<D, ScalarT>
uniform(Rng& rng, pos<D, ScalarT> const& a, pos<D, ScalarT> const& b)
Philip Trettner's avatar
Philip Trettner committed
224
225
226
{
    return mix(a, b, detail::uniform01<ScalarT>(rng));
}
Kersten Schuster's avatar
Kersten Schuster committed
227

228
template <int D, class ScalarT, class Rng, class = enable_if<is_floating_point<ScalarT>>>
229
TG_NODISCARD constexpr pos<D, ScalarT> uniform(Rng& rng, triangle<D, ScalarT> const& t)
Kersten Schuster's avatar
Kersten Schuster committed
230
{
231
232
    auto e0 = t.pos1 - t.pos0;
    auto e1 = t.pos2 - t.pos0;
Kersten Schuster's avatar
Kersten Schuster committed
233
234
    auto u0 = uniform(rng, ScalarT(0), ScalarT(1));
    auto u1 = uniform(rng, ScalarT(0), ScalarT(1));
235
    if (u0 + u1 > ScalarT(1))
Kersten Schuster's avatar
Kersten Schuster committed
236
237
238
239
    {
        u0 = ScalarT(1) - u0;
        u1 = ScalarT(1) - u1;
    }
240
    return t.pos0 + u0 * e0 + u1 * e1;
Kersten Schuster's avatar
Kersten Schuster committed
241
}
242
243

template <class Rng, class... Args>
244
TG_NODISCARD constexpr auto uniform_vec(Rng& rng, Args const&... args) -> decltype(uniform(rng, args...) - decltype(uniform(rng, args...))::zero)
245
246
247
248
{
    return uniform(rng, args...) - decltype(uniform(rng, args...))::zero;
}

Philip Trettner's avatar
Philip Trettner committed
249
250
251
252
253
254
255
256
257
258
259
260

namespace detail
{
template <>
struct sampler<bool>
{
    template <class Rng>
    constexpr static bool uniform(Rng& rng)
    {
        return rng() & 1;
    }
};
261
template <class T>
262
struct sampler<angle_t<T>>
263
264
{
    template <class Rng>
265
    constexpr static angle_t<T> uniform(Rng& rng)
266
267
268
269
    {
        return tg::uniform(rng, tg::radians(T(0)), 2 * tg::pi<T>);
    }
};
Philip Trettner's avatar
Philip Trettner committed
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
template <class T>
struct sampler<color<3, T>>
{
    template <class Rng>
    constexpr static color<3, T> uniform(Rng& rng)
    {
        return tg::color3(tg::uniform(rng, T(0), T(1)), tg::uniform(rng, T(0), T(1)), tg::uniform(rng, T(0), T(1)));
    }
};
template <class T>
struct sampler<color<4, T>>
{
    template <class Rng>
    constexpr static color<4, T> uniform(Rng& rng)
    {
        return tg::color3(tg::uniform(rng, T(0), T(1)), tg::uniform(rng, T(0), T(1)), tg::uniform(rng, T(0), T(1)), tg::uniform(rng, T(0), T(1)));
    }
};
Philip Trettner's avatar
Philip Trettner committed
288
289
290
291
292
293
template <int D, class ScalarT>
struct sampler<dir<D, ScalarT>>
{
    template <class Rng>
    constexpr static dir<D, ScalarT> uniform(Rng& rng)
    {
Philip Trettner's avatar
Philip Trettner committed
294
        auto ub = tg::aabb<D, ScalarT>::minus_one_to_one;
Philip Trettner's avatar
Philip Trettner committed
295
296
297
        while (true)
        {
            auto p = uniform_vec(rng, ub);
298
            auto l = length_sqr(p);
Philip Trettner's avatar
Philip Trettner committed
299
300
301
302
303
304
305
            if (l > ScalarT(0) && l <= ScalarT(1))
                return tg::dir<D, ScalarT>(p / tg::sqrt(l));
        }
    }
};
}

Philip Trettner's avatar
Philip Trettner committed
306
} // namespace tg