Coverage Report

Created: 2026-10-28 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibGfx/VectorN.h
Line
Count
Source
1
/*
2
 * Copyright (c) 2020, Stephan Unverwerth <s.unverwerth@serenityos.org>
3
 * Copyright (c) 2022, Jelle Raaijmakers <jelle@gmta.nl>
4
 * Copyright (c) 2022, the SerenityOS developers.
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 */
8
9
#pragma once
10
11
#include <AK/Array.h>
12
#include <AK/ByteString.h>
13
#include <AK/Error.h>
14
#include <AK/Math/Rounding.h>
15
#include <AK/Math/Sqrt.h>
16
#include <AK/StdLibExtras.h>
17
#include <AK/StringView.h>
18
19
#define LOOP_UNROLL_N 4
20
21
#define STRINGIFY_HELPER(x) #x
22
#define STRINGIFY(x) STRINGIFY_HELPER(x)
23
24
#if defined(AK_COMPILER_CLANG)
25
0
#    define UNROLL_LOOP _Pragma(STRINGIFY(unroll))
26
#else
27
#    define UNROLL_LOOP _Pragma(STRINGIFY(GCC unroll(LOOP_UNROLL_N)))
28
#endif
29
30
namespace Gfx {
31
32
template<size_t N, typename T>
33
requires(N >= 2 && N <= 4) class VectorN final {
34
    static_assert(LOOP_UNROLL_N >= N, "Unroll the entire loop for performance.");
35
36
public:
37
    [[nodiscard]] constexpr VectorN() = default;
38
    [[nodiscard]] constexpr VectorN(T x, T y)
39
    requires(N == 2)
40
0
        : m_data { x, y }
41
0
    {
42
0
    }
43
    [[nodiscard]] constexpr VectorN(T x, T y, T z)
44
    requires(N == 3)
45
0
        : m_data { x, y, z }
46
0
    {
47
0
    }
Unexecuted instantiation: _ZN3Gfx7VectorNILm3EfEC2EfffQeqT_Li3E
Unexecuted instantiation: _ZN3Gfx7VectorNILm3EiEC2EiiiQeqT_Li3E
Unexecuted instantiation: _ZN3Gfx7VectorNILm3EdEC2EdddQeqT_Li3E
48
    [[nodiscard]] constexpr VectorN(T x, T y, T z, T w)
49
    requires(N == 4)
50
0
        : m_data { x, y, z, w }
51
0
    {
52
0
    }
Unexecuted instantiation: _ZN3Gfx7VectorNILm4EfEC2EffffQeqT_Li4E
Unexecuted instantiation: _ZN3Gfx7VectorNILm4EdEC2EddddQeqT_Li4E
53
54
0
    [[nodiscard]] constexpr T x() const { return m_data[0]; }
Unexecuted instantiation: Gfx::VectorN<3ul, float>::x() const
Unexecuted instantiation: Gfx::VectorN<2ul, float>::x() const
Unexecuted instantiation: Gfx::VectorN<4ul, float>::x() const
Unexecuted instantiation: Gfx::VectorN<4ul, double>::x() const
Unexecuted instantiation: Gfx::VectorN<3ul, double>::x() const
55
0
    [[nodiscard]] constexpr T y() const { return m_data[1]; }
Unexecuted instantiation: Gfx::VectorN<3ul, float>::y() const
Unexecuted instantiation: Gfx::VectorN<2ul, float>::y() const
Unexecuted instantiation: Gfx::VectorN<4ul, float>::y() const
Unexecuted instantiation: Gfx::VectorN<4ul, double>::y() const
Unexecuted instantiation: Gfx::VectorN<3ul, double>::y() const
56
    [[nodiscard]] constexpr T z() const
57
    requires(N >= 3)
58
0
    {
59
0
        return m_data[2];
60
0
    }
Unexecuted instantiation: _ZNK3Gfx7VectorNILm3EfE1zEvQgeT_Li3E
Unexecuted instantiation: _ZNK3Gfx7VectorNILm4EfE1zEvQgeT_Li3E
Unexecuted instantiation: _ZNK3Gfx7VectorNILm4EdE1zEvQgeT_Li3E
Unexecuted instantiation: _ZNK3Gfx7VectorNILm3EdE1zEvQgeT_Li3E
61
    [[nodiscard]] constexpr T w() const
62
    requires(N >= 4)
63
0
    {
64
0
        return m_data[3];
65
0
    }
Unexecuted instantiation: _ZNK3Gfx7VectorNILm4EfE1wEvQgeT_Li4E
Unexecuted instantiation: _ZNK3Gfx7VectorNILm4EdE1wEvQgeT_Li4E
66
67
    constexpr void set_x(T value) { m_data[0] = value; }
68
    constexpr void set_y(T value) { m_data[1] = value; }
69
    constexpr void set_z(T value)
70
    requires(N >= 3)
71
    {
72
        m_data[2] = value;
73
    }
74
    constexpr void set_w(T value)
75
    requires(N >= 4)
76
    {
77
        m_data[3] = value;
78
    }
79
80
    [[nodiscard]] constexpr T const& operator[](size_t index) const
81
0
    {
82
0
        VERIFY(index < N);
83
0
        return m_data[index];
84
0
    }
Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator[](unsigned long) const
Unexecuted instantiation: Gfx::VectorN<3ul, int>::operator[](unsigned long) const
Unexecuted instantiation: Gfx::VectorN<4ul, float>::operator[](unsigned long) const
85
86
    [[nodiscard]] constexpr T& operator[](size_t index)
87
0
    {
88
0
        VERIFY(index < N);
89
0
        return m_data[index];
90
0
    }
Unexecuted instantiation: Gfx::VectorN<3ul, int>::operator[](unsigned long)
Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator[](unsigned long)
Unexecuted instantiation: Gfx::VectorN<4ul, float>::operator[](unsigned long)
91
92
    constexpr VectorN& operator+=(VectorN const& other)
93
0
    {
94
0
        UNROLL_LOOP
95
0
        for (auto i = 0u; i < N; ++i)
96
0
            m_data[i] += other.data()[i];
97
0
        return *this;
98
0
    }
99
100
    constexpr VectorN& operator-=(VectorN const& other)
101
    {
102
        UNROLL_LOOP
103
        for (auto i = 0u; i < N; ++i)
104
            m_data[i] -= other.data()[i];
105
        return *this;
106
    }
107
108
    constexpr VectorN& operator*=(T const& t)
109
0
    {
110
0
        UNROLL_LOOP
111
0
        for (auto i = 0u; i < N; ++i)
112
0
            m_data[i] *= t;
113
0
        return *this;
114
0
    }
Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator*=(float const&)
Unexecuted instantiation: Gfx::VectorN<2ul, float>::operator*=(float const&)
Unexecuted instantiation: Gfx::VectorN<3ul, double>::operator*=(double const&)
115
116
    [[nodiscard]] constexpr VectorN operator+(VectorN const& other) const
117
0
    {
118
0
        VectorN result;
119
0
        UNROLL_LOOP
120
0
        for (auto i = 0u; i < N; ++i)
121
0
            result.m_data[i] = m_data[i] + other.data()[i];
122
0
        return result;
123
0
    }
Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator+(Gfx::VectorN<3ul, float> const&) const
Unexecuted instantiation: Gfx::VectorN<4ul, float>::operator+(Gfx::VectorN<4ul, float> const&) const
124
125
    [[nodiscard]] constexpr VectorN operator-(VectorN const& other) const
126
0
    {
127
0
        VectorN result;
128
0
        UNROLL_LOOP
129
0
        for (auto i = 0u; i < N; ++i)
130
0
            result.m_data[i] = m_data[i] - other.data()[i];
131
0
        return result;
132
0
    }
Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator-(Gfx::VectorN<3ul, float> const&) const
Unexecuted instantiation: Gfx::VectorN<4ul, float>::operator-(Gfx::VectorN<4ul, float> const&) const
133
134
    [[nodiscard]] constexpr VectorN operator*(VectorN const& other) const
135
0
    {
136
0
        VectorN result;
137
0
        UNROLL_LOOP
138
0
        for (auto i = 0u; i < N; ++i)
139
0
            result.m_data[i] = m_data[i] * other.data()[i];
140
0
        return result;
141
0
    }
Unexecuted instantiation: Gfx::VectorN<4ul, float>::operator*(Gfx::VectorN<4ul, float> const&) const
Unexecuted instantiation: Gfx::VectorN<3ul, float>::operator*(Gfx::VectorN<3ul, float> const&) const
142
143
    [[nodiscard]] constexpr VectorN operator-() const
144
    {
145
        VectorN result;
146
        UNROLL_LOOP
147
        for (auto i = 0u; i < N; ++i)
148
            result.m_data[i] = -m_data[i];
149
        return result;
150
    }
151
152
    [[nodiscard]] constexpr VectorN operator/(VectorN const& other) const
153
0
    {
154
0
        VectorN result;
155
0
        UNROLL_LOOP
156
0
        for (auto i = 0u; i < N; ++i)
157
0
            result.m_data[i] = m_data[i] / other.data()[i];
158
0
        return result;
159
0
    }
160
161
    template<typename U>
162
    [[nodiscard]] constexpr VectorN operator+(U f) const
163
    {
164
        VectorN result;
165
        UNROLL_LOOP
166
        for (auto i = 0u; i < N; ++i)
167
            result.m_data[i] = m_data[i] + f;
168
        return result;
169
    }
170
171
    template<typename U>
172
    [[nodiscard]] constexpr VectorN operator-(U f) const
173
    {
174
        VectorN result;
175
        UNROLL_LOOP
176
        for (auto i = 0u; i < N; ++i)
177
            result.m_data[i] = m_data[i] - f;
178
        return result;
179
    }
180
181
    template<typename U>
182
    [[nodiscard]] constexpr VectorN operator*(U f) const
183
0
    {
184
0
        VectorN result;
185
0
        UNROLL_LOOP
186
0
        for (auto i = 0u; i < N; ++i)
187
0
            result.m_data[i] = m_data[i] * f;
188
0
        return result;
189
0
    }
Unexecuted instantiation: Gfx::VectorN<3ul, float> Gfx::VectorN<3ul, float>::operator*<int>(int) const
Unexecuted instantiation: Gfx::VectorN<3ul, float> Gfx::VectorN<3ul, float>::operator*<float>(float) const
Unexecuted instantiation: Gfx::VectorN<2ul, float> Gfx::VectorN<2ul, float>::operator*<float>(float) const
Unexecuted instantiation: Gfx::VectorN<4ul, float> Gfx::VectorN<4ul, float>::operator*<float>(float) const
190
191
    template<typename U>
192
    [[nodiscard]] constexpr VectorN operator/(U f) const
193
0
    {
194
0
        VectorN result;
195
0
        UNROLL_LOOP
196
0
        for (auto i = 0u; i < N; ++i)
197
0
            result.m_data[i] = m_data[i] / f;
198
0
        return result;
199
0
    }
200
201
    template<ConvertibleTo<T> U>
202
    constexpr bool operator==(VectorN<N, U> const& other) const
203
    {
204
        UNROLL_LOOP
205
        for (auto i = 0u; i < N; ++i) {
206
            if (m_data[i] != static_cast<T>(other.m_data[i]))
207
                return false;
208
        }
209
        return true;
210
    }
211
212
    [[nodiscard]] constexpr T dot(VectorN const& other) const
213
0
    {
214
0
        T result {};
215
0
        UNROLL_LOOP
216
0
        for (auto i = 0u; i < N; ++i)
217
0
            result += m_data[i] * other.data()[i];
218
0
        return result;
219
0
    }
Unexecuted instantiation: Gfx::VectorN<2ul, float>::dot(Gfx::VectorN<2ul, float> const&) const
Unexecuted instantiation: Gfx::VectorN<3ul, float>::dot(Gfx::VectorN<3ul, float> const&) const
Unexecuted instantiation: Gfx::VectorN<4ul, float>::dot(Gfx::VectorN<4ul, float> const&) const
Unexecuted instantiation: Gfx::VectorN<3ul, double>::dot(Gfx::VectorN<3ul, double> const&) const
220
221
    [[nodiscard]] constexpr VectorN cross(VectorN const& other) const
222
    requires(N == 3)
223
0
    {
224
0
        return VectorN(
225
0
            y() * other.z() - z() * other.y(),
226
0
            z() * other.x() - x() * other.z(),
227
0
            x() * other.y() - y() * other.x());
228
0
    }
229
230
    [[nodiscard]] constexpr VectorN normalized() const
231
0
    {
232
0
        VectorN copy { *this };
233
0
        copy.normalize();
234
0
        return copy;
235
0
    }
Unexecuted instantiation: Gfx::VectorN<2ul, float>::normalized() const
Unexecuted instantiation: Gfx::VectorN<3ul, double>::normalized() const
236
237
    [[nodiscard]] constexpr VectorN clamped(T m, T x) const
238
0
    {
239
0
        VectorN copy { *this };
240
0
        copy.clamp(m, x);
241
0
        return copy;
242
0
    }
243
244
    constexpr void clamp(T min_value, T max_value)
245
0
    {
246
0
        UNROLL_LOOP
247
0
        for (auto i = 0u; i < N; ++i) {
248
0
            m_data[i] = max(min_value, m_data[i]);
249
0
            m_data[i] = min(max_value, m_data[i]);
250
0
        }
251
0
    }
Unexecuted instantiation: Gfx::VectorN<3ul, float>::clamp(float, float)
Unexecuted instantiation: Gfx::VectorN<4ul, float>::clamp(float, float)
252
253
    constexpr void normalize()
254
0
    {
255
0
        T const inv_length = 1 / length();
256
0
        operator*=(inv_length);
257
0
    }
Unexecuted instantiation: Gfx::VectorN<2ul, float>::normalize()
Unexecuted instantiation: Gfx::VectorN<3ul, float>::normalize()
Unexecuted instantiation: Gfx::VectorN<3ul, double>::normalize()
258
259
    template<typename O = T>
260
    [[nodiscard]] constexpr O length() const
261
0
    {
262
0
        return AK::sqrt<O>(dot(*this));
263
0
    }
Unexecuted instantiation: float Gfx::VectorN<2ul, float>::length<float>() const
Unexecuted instantiation: float Gfx::VectorN<3ul, float>::length<float>() const
Unexecuted instantiation: double Gfx::VectorN<3ul, double>::length<double>() const
264
265
    [[nodiscard]] constexpr VectorN<2, T> xy() const
266
    requires(N >= 3)
267
    {
268
        return VectorN<2, T>(x(), y());
269
    }
270
271
    [[nodiscard]] constexpr VectorN<3, T> xyz() const
272
    requires(N >= 4)
273
    {
274
        return VectorN<3, T>(x(), y(), z());
275
    }
276
277
    [[nodiscard]] ByteString to_byte_string() const
278
    {
279
        if constexpr (N == 2)
280
            return ByteString::formatted("[{},{}]", x(), y());
281
        else if constexpr (N == 3)
282
            return ByteString::formatted("[{},{},{}]", x(), y(), z());
283
        else
284
            return ByteString::formatted("[{},{},{},{}]", x(), y(), z(), w());
285
    }
286
287
    template<typename U>
288
    [[nodiscard]] VectorN<N, U> to_type() const
289
    {
290
        VectorN<N, U> result;
291
        UNROLL_LOOP
292
        for (auto i = 0u; i < N; ++i)
293
            result.data()[i] = static_cast<U>(m_data[i]);
294
        return result;
295
    }
296
297
    template<typename U>
298
    [[nodiscard]] VectorN<N, U> to_rounded() const
299
    {
300
        VectorN<N, U> result;
301
        UNROLL_LOOP
302
        for (auto i = 0u; i < N; ++i)
303
            result.data()[i] = round_to<U>(m_data[i]);
304
        return result;
305
    }
306
307
    constexpr auto& data() { return m_data; }
308
0
    constexpr auto const& data() const { return m_data; }
Unexecuted instantiation: Gfx::VectorN<3ul, float>::data() const
Unexecuted instantiation: Gfx::VectorN<2ul, float>::data() const
Unexecuted instantiation: Gfx::VectorN<4ul, float>::data() const
Unexecuted instantiation: Gfx::VectorN<3ul, double>::data() const
309
310
private:
311
    Array<T, N> m_data;
312
};
313
314
}
315
316
namespace AK {
317
318
template<size_t N, typename T>
319
constexpr Gfx::VectorN<N, T> min(Gfx::VectorN<N, T> const& a, Gfx::VectorN<N, T> const& b)
320
{
321
    Gfx::VectorN<N, T> result;
322
    UNROLL_LOOP
323
    for (auto i = 0u; i < N; ++i)
324
        result[i] = min(a[i], b[i]);
325
    return result;
326
}
327
328
template<size_t N, typename T>
329
constexpr Gfx::VectorN<N, T> max(Gfx::VectorN<N, T> const& a, Gfx::VectorN<N, T> const& b)
330
{
331
    Gfx::VectorN<N, T> result;
332
    UNROLL_LOOP
333
    for (auto i = 0u; i < N; ++i)
334
        result[i] = max(a[i], b[i]);
335
    return result;
336
}
337
338
}