Coverage Report

Created: 2026-06-18 07:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/moxcms-0.8.1/src/nd_array.rs
Line
Count
Source
1
/*
2
 * // Copyright (c) Radzivon Bartoshyk 2/2025. All rights reserved.
3
 * //
4
 * // Redistribution and use in source and binary forms, with or without modification,
5
 * // are permitted provided that the following conditions are met:
6
 * //
7
 * // 1.  Redistributions of source code must retain the above copyright notice, this
8
 * // list of conditions and the following disclaimer.
9
 * //
10
 * // 2.  Redistributions in binary form must reproduce the above copyright notice,
11
 * // this list of conditions and the following disclaimer in the documentation
12
 * // and/or other materials provided with the distribution.
13
 * //
14
 * // 3.  Neither the name of the copyright holder nor the names of its
15
 * // contributors may be used to endorse or promote products derived from
16
 * // this software without specific prior written permission.
17
 * //
18
 * // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19
 * // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20
 * // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22
 * // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
 * // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25
 * // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26
 * // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27
 * // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29
use crate::math::{FusedMultiplyAdd, FusedMultiplyNegAdd};
30
use crate::mlaf::{mlaf, neg_mlaf};
31
use crate::safe_math::{SafeAdd, SafeMul};
32
use crate::{CmsError, MalformedSize, Vector3f, Vector4f};
33
use std::ops::{Add, Mul, Sub};
34
35
impl FusedMultiplyAdd<f32> for f32 {
36
    #[inline(always)]
37
0
    fn mla(&self, b: f32, c: f32) -> f32 {
38
0
        mlaf(*self, b, c)
39
0
    }
40
}
41
42
impl FusedMultiplyNegAdd<f32> for f32 {
43
    #[inline(always)]
44
0
    fn neg_mla(&self, b: f32, c: f32) -> f32 {
45
0
        neg_mlaf(*self, b, c)
46
0
    }
47
}
48
49
#[inline(always)]
50
0
pub(crate) fn lerp<
51
0
    T: Mul<Output = T>
52
0
        + Sub<Output = T>
53
0
        + Add<Output = T>
54
0
        + From<f32>
55
0
        + Copy
56
0
        + FusedMultiplyAdd<T>
57
0
        + FusedMultiplyNegAdd<T>,
58
0
>(
59
0
    a: T,
60
0
    b: T,
61
0
    t: T,
62
0
) -> T {
63
0
    a.neg_mla(a, t).mla(b, t)
64
0
}
65
66
/// 4D CLUT helper.
67
///
68
/// Represents hypercube.
69
pub struct Hypercube<'a> {
70
    array: &'a [f32],
71
    x_stride: u32,
72
    y_stride: u32,
73
    z_stride: u32,
74
    grid_size: [u8; 4],
75
}
76
77
trait Fetcher4<T> {
78
    fn fetch(&self, x: i32, y: i32, z: i32, w: i32) -> T;
79
}
80
81
impl Hypercube<'_> {
82
0
    pub fn new(
83
0
        array: &[f32],
84
0
        grid_size: usize,
85
0
        channels: usize,
86
0
    ) -> Result<Hypercube<'_>, CmsError> {
87
0
        if array.is_empty() || grid_size == 0 {
88
0
            return Ok(Hypercube {
89
0
                array,
90
0
                x_stride: 0,
91
0
                y_stride: 0,
92
0
                z_stride: 0,
93
0
                grid_size: [0, 0, 0, 0],
94
0
            });
95
0
        }
96
0
        let z_stride = grid_size as u32;
97
0
        let y_stride = z_stride * z_stride;
98
0
        let x_stride = z_stride * z_stride * z_stride;
99
100
0
        let last_index = (grid_size - 1)
101
0
            .safe_mul(x_stride as usize)?
102
0
            .safe_add((grid_size - 1).safe_mul(y_stride as usize)?)?
103
0
            .safe_add((grid_size - 1).safe_mul(z_stride as usize)?)?
104
0
            .safe_add(grid_size - 1)?
105
0
            .safe_mul(channels)?;
106
107
0
        if last_index >= array.len() {
108
0
            return Err(CmsError::MalformedClut(MalformedSize {
109
0
                size: array.len(),
110
0
                expected: last_index,
111
0
            }));
112
0
        }
113
114
0
        Ok(Hypercube {
115
0
            array,
116
0
            x_stride,
117
0
            y_stride,
118
0
            z_stride,
119
0
            grid_size: [
120
0
                grid_size as u8,
121
0
                grid_size as u8,
122
0
                grid_size as u8,
123
0
                grid_size as u8,
124
0
            ],
125
0
        })
126
0
    }
127
128
0
    pub fn new_hypercube(
129
0
        array: &[f32],
130
0
        grid_size: [u8; 4],
131
0
        channels: usize,
132
0
    ) -> Result<Hypercube<'_>, CmsError> {
133
0
        if array.is_empty()
134
0
            || grid_size[0] == 0
135
0
            || grid_size[1] == 0
136
0
            || grid_size[2] == 0
137
0
            || grid_size[3] == 0
138
        {
139
0
            return Ok(Hypercube {
140
0
                array,
141
0
                x_stride: 0,
142
0
                y_stride: 0,
143
0
                z_stride: 0,
144
0
                grid_size,
145
0
            });
146
0
        }
147
0
        let z_stride = grid_size[2] as u32;
148
0
        let y_stride = z_stride * grid_size[1] as u32;
149
0
        let x_stride = y_stride * grid_size[0] as u32;
150
0
        let last_index = (grid_size[0] as usize - 1)
151
0
            .safe_mul(x_stride as usize)?
152
0
            .safe_add((grid_size[1] as usize - 1).safe_mul(y_stride as usize)?)?
153
0
            .safe_add((grid_size[2] as usize - 1).safe_mul(z_stride as usize)?)?
154
0
            .safe_add(grid_size[3] as usize - 1)?
155
0
            .safe_mul(channels)?;
156
157
0
        if last_index >= array.len() {
158
0
            return Err(CmsError::MalformedClut(MalformedSize {
159
0
                size: array.len(),
160
0
                expected: last_index,
161
0
            }));
162
0
        }
163
164
0
        Ok(Hypercube {
165
0
            array,
166
0
            x_stride,
167
0
            y_stride,
168
0
            z_stride,
169
0
            grid_size,
170
0
        })
171
0
    }
172
}
173
174
struct Fetch4Vec3<'a> {
175
    array: &'a [f32],
176
    x_stride: u32,
177
    y_stride: u32,
178
    z_stride: u32,
179
}
180
181
struct Fetch4Vec4<'a> {
182
    array: &'a [f32],
183
    x_stride: u32,
184
    y_stride: u32,
185
    z_stride: u32,
186
}
187
188
impl Fetcher4<Vector3f> for Fetch4Vec3<'_> {
189
    #[inline(always)]
190
0
    fn fetch(&self, x: i32, y: i32, z: i32, w: i32) -> Vector3f {
191
0
        let start = (x as u32 * self.x_stride
192
0
            + y as u32 * self.y_stride
193
0
            + z as u32 * self.z_stride
194
0
            + w as u32) as usize
195
0
            * 3;
196
0
        let k = &self.array[start..start + 3];
197
0
        Vector3f {
198
0
            v: [k[0], k[1], k[2]],
199
0
        }
200
0
    }
201
}
202
203
impl Fetcher4<Vector4f> for Fetch4Vec4<'_> {
204
    #[inline(always)]
205
0
    fn fetch(&self, x: i32, y: i32, z: i32, w: i32) -> Vector4f {
206
0
        let start = (x as u32 * self.x_stride
207
0
            + y as u32 * self.y_stride
208
0
            + z as u32 * self.z_stride
209
0
            + w as u32) as usize
210
0
            * 4;
211
0
        let k = &self.array[start..start + 4];
212
0
        Vector4f {
213
0
            v: [k[0], k[1], k[2], k[3]],
214
0
        }
215
0
    }
216
}
217
218
impl Hypercube<'_> {
219
    #[inline(always)]
220
0
    fn quadlinear<
221
0
        T: From<f32>
222
0
            + Add<T, Output = T>
223
0
            + Mul<T, Output = T>
224
0
            + FusedMultiplyAdd<T>
225
0
            + Sub<T, Output = T>
226
0
            + Copy
227
0
            + FusedMultiplyNegAdd<T>,
228
0
    >(
229
0
        &self,
230
0
        lin_x: f32,
231
0
        lin_y: f32,
232
0
        lin_z: f32,
233
0
        lin_w: f32,
234
0
        r: impl Fetcher4<T>,
235
0
    ) -> T {
236
0
        let lin_x = lin_x.max(0.0);
237
0
        let lin_y = lin_y.max(0.0);
238
0
        let lin_z = lin_z.max(0.0);
239
0
        let lin_w = lin_w.max(0.0);
240
241
0
        let scale_x = (self.grid_size[0] as i32 - 1) as f32;
242
0
        let scale_y = (self.grid_size[1] as i32 - 1) as f32;
243
0
        let scale_z = (self.grid_size[2] as i32 - 1) as f32;
244
0
        let scale_w = (self.grid_size[3] as i32 - 1) as f32;
245
246
0
        let x = (lin_x * scale_x).floor().min(scale_x) as i32;
247
0
        let y = (lin_y * scale_y).floor().min(scale_y) as i32;
248
0
        let z = (lin_z * scale_z).floor().min(scale_z) as i32;
249
0
        let w = (lin_w * scale_w).floor().min(scale_w) as i32;
250
251
0
        let x_n = (lin_x * scale_x).ceil().min(scale_x) as i32;
252
0
        let y_n = (lin_y * scale_y).ceil().min(scale_y) as i32;
253
0
        let z_n = (lin_z * scale_z).ceil().min(scale_z) as i32;
254
0
        let w_n = (lin_w * scale_w).ceil().min(scale_w) as i32;
255
256
0
        let x_d = T::from(lin_x * scale_x - x as f32);
257
0
        let y_d = T::from(lin_y * scale_y - y as f32);
258
0
        let z_d = T::from(lin_z * scale_z - z as f32);
259
0
        let w_d = T::from(lin_w * scale_w - w as f32);
260
261
0
        let r_x1 = lerp(r.fetch(x, y, z, w), r.fetch(x_n, y, z, w), x_d);
262
0
        let r_x2 = lerp(r.fetch(x, y_n, z, w), r.fetch(x_n, y_n, z, w), x_d);
263
0
        let r_y1 = lerp(r_x1, r_x2, y_d);
264
0
        let r_x3 = lerp(r.fetch(x, y, z_n, w), r.fetch(x_n, y, z_n, w), x_d);
265
0
        let r_x4 = lerp(r.fetch(x, y_n, z_n, w), r.fetch(x_n, y_n, z_n, w), x_d);
266
0
        let r_y2 = lerp(r_x3, r_x4, y_d);
267
0
        let r_z1 = lerp(r_y1, r_y2, z_d);
268
269
0
        let r_x1 = lerp(r.fetch(x, y, z, w_n), r.fetch(x_n, y, z, w_n), x_d);
270
0
        let r_x2 = lerp(r.fetch(x, y_n, z, w_n), r.fetch(x_n, y_n, z, w_n), x_d);
271
0
        let r_y1 = lerp(r_x1, r_x2, y_d);
272
0
        let r_x3 = lerp(r.fetch(x, y, z_n, w_n), r.fetch(x_n, y, z_n, w_n), x_d);
273
0
        let r_x4 = lerp(r.fetch(x, y_n, z_n, w_n), r.fetch(x_n, y_n, z_n, w_n), x_d);
274
0
        let r_y2 = lerp(r_x3, r_x4, y_d);
275
0
        let r_z2 = lerp(r_y1, r_y2, z_d);
276
0
        lerp(r_z1, r_z2, w_d)
277
0
    }
278
279
    #[inline]
280
0
    pub fn quadlinear_vec3(&self, lin_x: f32, lin_y: f32, lin_z: f32, lin_w: f32) -> Vector3f {
281
0
        self.quadlinear(
282
0
            lin_x,
283
0
            lin_y,
284
0
            lin_z,
285
0
            lin_w,
286
0
            Fetch4Vec3 {
287
0
                array: self.array,
288
0
                x_stride: self.x_stride,
289
0
                y_stride: self.y_stride,
290
0
                z_stride: self.z_stride,
291
0
            },
292
        )
293
0
    }
294
295
    #[inline]
296
0
    pub fn quadlinear_vec4(&self, lin_x: f32, lin_y: f32, lin_z: f32, lin_w: f32) -> Vector4f {
297
0
        self.quadlinear(
298
0
            lin_x,
299
0
            lin_y,
300
0
            lin_z,
301
0
            lin_w,
302
0
            Fetch4Vec4 {
303
0
                array: self.array,
304
0
                x_stride: self.x_stride,
305
0
                y_stride: self.y_stride,
306
0
                z_stride: self.z_stride,
307
0
            },
308
        )
309
0
    }
310
311
    #[cfg(feature = "options")]
312
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
313
    #[inline(always)]
314
    fn pyramid<
315
        T: From<f32>
316
            + Add<T, Output = T>
317
            + Mul<T, Output = T>
318
            + FusedMultiplyAdd<T>
319
            + Sub<T, Output = T>
320
            + Copy
321
            + FusedMultiplyNegAdd<T>,
322
    >(
323
        &self,
324
        lin_x: f32,
325
        lin_y: f32,
326
        lin_z: f32,
327
        lin_w: f32,
328
        r: impl Fetcher4<T>,
329
    ) -> T {
330
        let lin_x = lin_x.max(0.0);
331
        let lin_y = lin_y.max(0.0);
332
        let lin_z = lin_z.max(0.0);
333
        let lin_w = lin_w.max(0.0);
334
335
        let scale_x = (self.grid_size[0] as i32 - 1) as f32;
336
        let scale_y = (self.grid_size[1] as i32 - 1) as f32;
337
        let scale_z = (self.grid_size[2] as i32 - 1) as f32;
338
        let scale_w = (self.grid_size[3] as i32 - 1) as f32;
339
340
        let x = (lin_x * scale_x).floor().min(scale_x) as i32;
341
        let y = (lin_y * scale_y).floor().min(scale_y) as i32;
342
        let z = (lin_z * scale_z).floor().min(scale_z) as i32;
343
        let w = (lin_w * scale_w).floor().min(scale_w) as i32;
344
345
        let x_n = (lin_x * scale_x).ceil().min(scale_x) as i32;
346
        let y_n = (lin_y * scale_y).ceil().min(scale_y) as i32;
347
        let z_n = (lin_z * scale_z).ceil().min(scale_z) as i32;
348
        let w_n = (lin_w * scale_w).ceil().min(scale_w) as i32;
349
350
        let dr = lin_x * scale_x - x as f32;
351
        let dg = lin_y * scale_y - y as f32;
352
        let db = lin_z * scale_z - z as f32;
353
        let dw = lin_w * scale_w - w as f32;
354
355
        let c0 = r.fetch(x, y, z, w);
356
357
        let w0 = if dr > db && dg > db {
358
            let x0 = r.fetch(x_n, y_n, z_n, w);
359
            let x1 = r.fetch(x_n, y_n, z, w);
360
            let x2 = r.fetch(x_n, y, z, w);
361
            let x3 = r.fetch(x, y_n, z, w);
362
363
            let c1 = x0 - x1;
364
            let c2 = x2 - c0;
365
            let c3 = x3 - c0;
366
            let c4 = c0 - x3 - x2 + x1;
367
368
            let s0 = c0.mla(c1, T::from(db));
369
            let s1 = s0.mla(c2, T::from(dr));
370
            let s2 = s1.mla(c3, T::from(dg));
371
            s2.mla(c4, T::from(dr * dg))
372
        } else if db > dr && dg > dr {
373
            let x0 = r.fetch(x, y, z_n, w);
374
            let x1 = r.fetch(x_n, y_n, z_n, w);
375
            let x2 = r.fetch(x, y_n, z_n, w);
376
            let x3 = r.fetch(x, y_n, z, w);
377
378
            let c1 = x0 - c0;
379
            let c2 = x1 - x2;
380
            let c3 = x3 - c0;
381
            let c4 = c0 - x3 - x0 + x2;
382
383
            let s0 = c0.mla(c1, T::from(db));
384
            let s1 = s0.mla(c2, T::from(dr));
385
            let s2 = s1.mla(c3, T::from(dg));
386
            s2.mla(c4, T::from(dg * db))
387
        } else {
388
            let x0 = r.fetch(x, y, z_n, w);
389
            let x1 = r.fetch(x_n, y, z, w);
390
            let x2 = r.fetch(x_n, y, z_n, w);
391
            let x3 = r.fetch(x_n, y_n, z_n, w);
392
393
            let c1 = x0 - c0;
394
            let c2 = x1 - c0;
395
            let c3 = x3 - x2;
396
            let c4 = c0 - x1 - x0 + x2;
397
398
            let s0 = c0.mla(c1, T::from(db));
399
            let s1 = s0.mla(c2, T::from(dr));
400
            let s2 = s1.mla(c3, T::from(dg));
401
            s2.mla(c4, T::from(db * dr))
402
        };
403
404
        let c0 = r.fetch(x, y, z, w_n);
405
406
        let w1 = if dr > db && dg > db {
407
            let x0 = r.fetch(x_n, y_n, z_n, w_n);
408
            let x1 = r.fetch(x_n, y_n, z, w_n);
409
            let x2 = r.fetch(x_n, y, z, w_n);
410
            let x3 = r.fetch(x, y_n, z, w_n);
411
412
            let c1 = x0 - x1;
413
            let c2 = x2 - c0;
414
            let c3 = x3 - c0;
415
            let c4 = c0 - x3 - x2 + x1;
416
417
            let s0 = c0.mla(c1, T::from(db));
418
            let s1 = s0.mla(c2, T::from(dr));
419
            let s2 = s1.mla(c3, T::from(dg));
420
            s2.mla(c4, T::from(dr * dg))
421
        } else if db > dr && dg > dr {
422
            let x0 = r.fetch(x, y, z_n, w_n);
423
            let x1 = r.fetch(x_n, y_n, z_n, w_n);
424
            let x2 = r.fetch(x, y_n, z_n, w_n);
425
            let x3 = r.fetch(x, y_n, z, w_n);
426
427
            let c1 = x0 - c0;
428
            let c2 = x1 - x2;
429
            let c3 = x3 - c0;
430
            let c4 = c0 - x3 - x0 + x2;
431
432
            let s0 = c0.mla(c1, T::from(db));
433
            let s1 = s0.mla(c2, T::from(dr));
434
            let s2 = s1.mla(c3, T::from(dg));
435
            s2.mla(c4, T::from(dg * db))
436
        } else {
437
            let x0 = r.fetch(x, y, z_n, w_n);
438
            let x1 = r.fetch(x_n, y, z, w_n);
439
            let x2 = r.fetch(x_n, y, z_n, w_n);
440
            let x3 = r.fetch(x_n, y_n, z_n, w_n);
441
442
            let c1 = x0 - c0;
443
            let c2 = x1 - c0;
444
            let c3 = x3 - x2;
445
            let c4 = c0 - x1 - x0 + x2;
446
447
            let s0 = c0.mla(c1, T::from(db));
448
            let s1 = s0.mla(c2, T::from(dr));
449
            let s2 = s1.mla(c3, T::from(dg));
450
            s2.mla(c4, T::from(db * dr))
451
        };
452
        w0.neg_mla(w0, T::from(dw)).mla(w1, T::from(dw))
453
    }
454
455
    #[cfg(feature = "options")]
456
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
457
    #[inline]
458
    pub fn pyramid_vec3(&self, lin_x: f32, lin_y: f32, lin_z: f32, lin_w: f32) -> Vector3f {
459
        self.pyramid(
460
            lin_x,
461
            lin_y,
462
            lin_z,
463
            lin_w,
464
            Fetch4Vec3 {
465
                array: self.array,
466
                x_stride: self.x_stride,
467
                y_stride: self.y_stride,
468
                z_stride: self.z_stride,
469
            },
470
        )
471
    }
472
473
    #[cfg(feature = "options")]
474
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
475
    #[inline]
476
    pub fn pyramid_vec4(&self, lin_x: f32, lin_y: f32, lin_z: f32, lin_w: f32) -> Vector4f {
477
        self.pyramid(
478
            lin_x,
479
            lin_y,
480
            lin_z,
481
            lin_w,
482
            Fetch4Vec4 {
483
                array: self.array,
484
                x_stride: self.x_stride,
485
                y_stride: self.y_stride,
486
                z_stride: self.z_stride,
487
            },
488
        )
489
    }
490
491
    #[cfg(feature = "options")]
492
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
493
    #[inline(always)]
494
    fn prism<
495
        T: From<f32>
496
            + Add<T, Output = T>
497
            + Mul<T, Output = T>
498
            + FusedMultiplyAdd<T>
499
            + Sub<T, Output = T>
500
            + Copy
501
            + FusedMultiplyNegAdd<T>,
502
    >(
503
        &self,
504
        lin_x: f32,
505
        lin_y: f32,
506
        lin_z: f32,
507
        lin_w: f32,
508
        r: impl Fetcher4<T>,
509
    ) -> T {
510
        let lin_x = lin_x.max(0.0);
511
        let lin_y = lin_y.max(0.0);
512
        let lin_z = lin_z.max(0.0);
513
        let lin_w = lin_w.max(0.0);
514
515
        let scale_x = (self.grid_size[0] as i32 - 1) as f32;
516
        let scale_y = (self.grid_size[1] as i32 - 1) as f32;
517
        let scale_z = (self.grid_size[2] as i32 - 1) as f32;
518
        let scale_w = (self.grid_size[3] as i32 - 1) as f32;
519
520
        let x = (lin_x * scale_x).floor().min(scale_x) as i32;
521
        let y = (lin_y * scale_y).floor().min(scale_y) as i32;
522
        let z = (lin_z * scale_z).floor().min(scale_z) as i32;
523
        let w = (lin_w * scale_w).floor().min(scale_w) as i32;
524
525
        let x_n = (lin_x * scale_x).ceil().min(scale_x) as i32;
526
        let y_n = (lin_y * scale_y).ceil().min(scale_y) as i32;
527
        let z_n = (lin_z * scale_z).ceil().min(scale_z) as i32;
528
        let w_n = (lin_w * scale_w).ceil().min(scale_w) as i32;
529
530
        let dr = lin_x * scale_x - x as f32;
531
        let dg = lin_y * scale_y - y as f32;
532
        let db = lin_z * scale_z - z as f32;
533
        let dw = lin_w * scale_w - w as f32;
534
535
        let c0 = r.fetch(x, y, z, w);
536
537
        let w0 = if db >= dr {
538
            let x0 = r.fetch(x, y, z_n, w);
539
            let x1 = r.fetch(x_n, y, z_n, w);
540
            let x2 = r.fetch(x, y_n, z, w);
541
            let x3 = r.fetch(x, y_n, z_n, w);
542
            let x4 = r.fetch(x_n, y_n, z_n, w);
543
544
            let c1 = x0 - c0;
545
            let c2 = x1 - x0;
546
            let c3 = x2 - c0;
547
            let c4 = c0 - x2 - x0 + x3;
548
            let c5 = x0 - x3 - x1 + x4;
549
550
            let s0 = c0.mla(c1, T::from(db));
551
            let s1 = s0.mla(c2, T::from(dr));
552
            let s2 = s1.mla(c3, T::from(dg));
553
            let s3 = s2.mla(c4, T::from(dg * db));
554
            s3.mla(c5, T::from(dr * dg))
555
        } else {
556
            let x0 = r.fetch(x_n, y, z, w);
557
            let x1 = r.fetch(x_n, y, z_n, w);
558
            let x2 = r.fetch(x, y_n, z, w);
559
            let x3 = r.fetch(x_n, y_n, z, w);
560
            let x4 = r.fetch(x_n, y_n, z_n, w);
561
562
            let c1 = x1 - x0;
563
            let c2 = x0 - c0;
564
            let c3 = x2 - c0;
565
            let c4 = x0 - x3 - x1 + x4;
566
            let c5 = c0 - x2 - x0 + x3;
567
568
            let s0 = c0.mla(c1, T::from(db));
569
            let s1 = s0.mla(c2, T::from(dr));
570
            let s2 = s1.mla(c3, T::from(dg));
571
            let s3 = s2.mla(c4, T::from(dg * db));
572
            s3.mla(c5, T::from(dr * dg))
573
        };
574
575
        let c0 = r.fetch(x, y, z, w_n);
576
577
        let w1 = if db >= dr {
578
            let x0 = r.fetch(x, y, z_n, w_n);
579
            let x1 = r.fetch(x_n, y, z_n, w_n);
580
            let x2 = r.fetch(x, y_n, z, w_n);
581
            let x3 = r.fetch(x, y_n, z_n, w_n);
582
            let x4 = r.fetch(x_n, y_n, z_n, w_n);
583
584
            let c1 = x0 - c0;
585
            let c2 = x1 - x0;
586
            let c3 = x2 - c0;
587
            let c4 = c0 - x2 - x0 + x3;
588
            let c5 = x0 - x3 - x1 + x4;
589
590
            let s0 = c0.mla(c1, T::from(db));
591
            let s1 = s0.mla(c2, T::from(dr));
592
            let s2 = s1.mla(c3, T::from(dg));
593
            let s3 = s2.mla(c4, T::from(dg * db));
594
            s3.mla(c5, T::from(dr * dg))
595
        } else {
596
            let x0 = r.fetch(x_n, y, z, w_n);
597
            let x1 = r.fetch(x_n, y, z_n, w_n);
598
            let x2 = r.fetch(x, y_n, z, w_n);
599
            let x3 = r.fetch(x_n, y_n, z, w_n);
600
            let x4 = r.fetch(x_n, y_n, z_n, w_n);
601
602
            let c1 = x1 - x0;
603
            let c2 = x0 - c0;
604
            let c3 = x2 - c0;
605
            let c4 = x0 - x3 - x1 + x4;
606
            let c5 = c0 - x2 - x0 + x3;
607
608
            let s0 = c0.mla(c1, T::from(db));
609
            let s1 = s0.mla(c2, T::from(dr));
610
            let s2 = s1.mla(c3, T::from(dg));
611
            let s3 = s2.mla(c4, T::from(dg * db));
612
            s3.mla(c5, T::from(dr * dg))
613
        };
614
        w0.neg_mla(w0, T::from(dw)).mla(w1, T::from(dw))
615
    }
616
617
    #[cfg(feature = "options")]
618
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
619
    #[inline]
620
    pub fn prism_vec3(&self, lin_x: f32, lin_y: f32, lin_z: f32, lin_w: f32) -> Vector3f {
621
        self.prism(
622
            lin_x,
623
            lin_y,
624
            lin_z,
625
            lin_w,
626
            Fetch4Vec3 {
627
                array: self.array,
628
                x_stride: self.x_stride,
629
                y_stride: self.y_stride,
630
                z_stride: self.z_stride,
631
            },
632
        )
633
    }
634
635
    #[cfg(feature = "options")]
636
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
637
    #[inline]
638
    pub fn prism_vec4(&self, lin_x: f32, lin_y: f32, lin_z: f32, lin_w: f32) -> Vector4f {
639
        self.prism(
640
            lin_x,
641
            lin_y,
642
            lin_z,
643
            lin_w,
644
            Fetch4Vec4 {
645
                array: self.array,
646
                x_stride: self.x_stride,
647
                y_stride: self.y_stride,
648
                z_stride: self.z_stride,
649
            },
650
        )
651
    }
652
653
    #[cfg(feature = "options")]
654
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
655
    #[inline(always)]
656
    fn tetra<
657
        T: From<f32>
658
            + Add<T, Output = T>
659
            + Mul<T, Output = T>
660
            + FusedMultiplyAdd<T>
661
            + Sub<T, Output = T>
662
            + Copy
663
            + FusedMultiplyNegAdd<T>,
664
    >(
665
        &self,
666
        lin_x: f32,
667
        lin_y: f32,
668
        lin_z: f32,
669
        lin_w: f32,
670
        r: impl Fetcher4<T>,
671
    ) -> T {
672
        let lin_x = lin_x.max(0.0);
673
        let lin_y = lin_y.max(0.0);
674
        let lin_z = lin_z.max(0.0);
675
        let lin_w = lin_w.max(0.0);
676
677
        let scale_x = (self.grid_size[0] as i32 - 1) as f32;
678
        let scale_y = (self.grid_size[1] as i32 - 1) as f32;
679
        let scale_z = (self.grid_size[2] as i32 - 1) as f32;
680
        let scale_w = (self.grid_size[3] as i32 - 1) as f32;
681
682
        let x = (lin_x * scale_x).floor().min(scale_x) as i32;
683
        let y = (lin_y * scale_y).floor().min(scale_y) as i32;
684
        let z = (lin_z * scale_z).floor().min(scale_z) as i32;
685
        let w = (lin_w * scale_w).floor().min(scale_w) as i32;
686
687
        let x_n = (lin_x * scale_x).ceil().min(scale_x) as i32;
688
        let y_n = (lin_y * scale_y).ceil().min(scale_y) as i32;
689
        let z_n = (lin_z * scale_z).ceil().min(scale_z) as i32;
690
        let w_n = (lin_w * scale_w).ceil().min(scale_w) as i32;
691
692
        let rx = lin_x * scale_x - x as f32;
693
        let ry = lin_y * scale_y - y as f32;
694
        let rz = lin_z * scale_z - z as f32;
695
        let rw = lin_w * scale_w - w as f32;
696
697
        let c0 = r.fetch(x, y, z, w);
698
        let c2;
699
        let c1;
700
        let c3;
701
        if rx >= ry {
702
            if ry >= rz {
703
                //rx >= ry && ry >= rz
704
                c1 = r.fetch(x_n, y, z, w) - c0;
705
                c2 = r.fetch(x_n, y_n, z, w) - r.fetch(x_n, y, z, w);
706
                c3 = r.fetch(x_n, y_n, z_n, w) - r.fetch(x_n, y_n, z, w);
707
            } else if rx >= rz {
708
                //rx >= rz && rz >= ry
709
                c1 = r.fetch(x_n, y, z, w) - c0;
710
                c2 = r.fetch(x_n, y_n, z_n, w) - r.fetch(x_n, y, z_n, w);
711
                c3 = r.fetch(x_n, y, z_n, w) - r.fetch(x_n, y, z, w);
712
            } else {
713
                //rz > rx && rx >= ry
714
                c1 = r.fetch(x_n, y, z_n, w) - r.fetch(x, y, z_n, w);
715
                c2 = r.fetch(x_n, y_n, z_n, w) - r.fetch(x_n, y, z_n, w);
716
                c3 = r.fetch(x, y, z_n, w) - c0;
717
            }
718
        } else if rx >= rz {
719
            //ry > rx && rx >= rz
720
            c1 = r.fetch(x_n, y_n, z, w) - r.fetch(x, y_n, z, w);
721
            c2 = r.fetch(x, y_n, z, w) - c0;
722
            c3 = r.fetch(x_n, y_n, z_n, w) - r.fetch(x_n, y_n, z, w);
723
        } else if ry >= rz {
724
            //ry >= rz && rz > rx
725
            c1 = r.fetch(x_n, y_n, z_n, w) - r.fetch(x, y_n, z_n, w);
726
            c2 = r.fetch(x, y_n, z, w) - c0;
727
            c3 = r.fetch(x, y_n, z_n, w) - r.fetch(x, y_n, z, w);
728
        } else {
729
            //rz > ry && ry > rx
730
            c1 = r.fetch(x_n, y_n, z_n, w) - r.fetch(x, y_n, z_n, w);
731
            c2 = r.fetch(x, y_n, z_n, w) - r.fetch(x, y, z_n, w);
732
            c3 = r.fetch(x, y, z_n, w) - c0;
733
        }
734
        let s0 = c0.mla(c1, T::from(rx));
735
        let s1 = s0.mla(c2, T::from(ry));
736
        let w0 = s1.mla(c3, T::from(rz));
737
738
        let c0 = r.fetch(x, y, z, w_n);
739
        let c2;
740
        let c1;
741
        let c3;
742
        if rx >= ry {
743
            if ry >= rz {
744
                //rx >= ry && ry >= rz
745
                c1 = r.fetch(x_n, y, z, w_n) - c0;
746
                c2 = r.fetch(x_n, y_n, z, w_n) - r.fetch(x_n, y, z, w_n);
747
                c3 = r.fetch(x_n, y_n, z_n, w_n) - r.fetch(x_n, y_n, z, w_n);
748
            } else if rx >= rz {
749
                //rx >= rz && rz >= ry
750
                c1 = r.fetch(x_n, y, z, w_n) - c0;
751
                c2 = r.fetch(x_n, y_n, z_n, w_n) - r.fetch(x_n, y, z_n, w_n);
752
                c3 = r.fetch(x_n, y, z_n, w_n) - r.fetch(x_n, y, z, w_n);
753
            } else {
754
                //rz > rx && rx >= ry
755
                c1 = r.fetch(x_n, y, z_n, w_n) - r.fetch(x, y, z_n, w_n);
756
                c2 = r.fetch(x_n, y_n, z_n, w_n) - r.fetch(x_n, y, z_n, w_n);
757
                c3 = r.fetch(x, y, z_n, w_n) - c0;
758
            }
759
        } else if rx >= rz {
760
            //ry > rx && rx >= rz
761
            c1 = r.fetch(x_n, y_n, z, w_n) - r.fetch(x, y_n, z, w_n);
762
            c2 = r.fetch(x, y_n, z, w_n) - c0;
763
            c3 = r.fetch(x_n, y_n, z_n, w_n) - r.fetch(x_n, y_n, z, w_n);
764
        } else if ry >= rz {
765
            //ry >= rz && rz > rx
766
            c1 = r.fetch(x_n, y_n, z_n, w_n) - r.fetch(x, y_n, z_n, w_n);
767
            c2 = r.fetch(x, y_n, z, w_n) - c0;
768
            c3 = r.fetch(x, y_n, z_n, w_n) - r.fetch(x, y_n, z, w_n);
769
        } else {
770
            //rz > ry && ry > rx
771
            c1 = r.fetch(x_n, y_n, z_n, w_n) - r.fetch(x, y_n, z_n, w_n);
772
            c2 = r.fetch(x, y_n, z_n, w_n) - r.fetch(x, y, z_n, w_n);
773
            c3 = r.fetch(x, y, z_n, w_n) - c0;
774
        }
775
        let s0 = c0.mla(c1, T::from(rx));
776
        let s1 = s0.mla(c2, T::from(ry));
777
        let w1 = s1.mla(c3, T::from(rz));
778
        w0.neg_mla(w0, T::from(rw)).mla(w1, T::from(rw))
779
    }
780
781
    #[cfg(feature = "options")]
782
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
783
    #[inline]
784
    pub fn tetra_vec3(&self, lin_x: f32, lin_y: f32, lin_z: f32, lin_w: f32) -> Vector3f {
785
        self.tetra(
786
            lin_x,
787
            lin_y,
788
            lin_z,
789
            lin_w,
790
            Fetch4Vec3 {
791
                array: self.array,
792
                x_stride: self.x_stride,
793
                y_stride: self.y_stride,
794
                z_stride: self.z_stride,
795
            },
796
        )
797
    }
798
799
    #[cfg(feature = "options")]
800
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
801
    #[inline]
802
    pub fn tetra_vec4(&self, lin_x: f32, lin_y: f32, lin_z: f32, lin_w: f32) -> Vector4f {
803
        self.tetra(
804
            lin_x,
805
            lin_y,
806
            lin_z,
807
            lin_w,
808
            Fetch4Vec4 {
809
                array: self.array,
810
                x_stride: self.x_stride,
811
                y_stride: self.y_stride,
812
                z_stride: self.z_stride,
813
            },
814
        )
815
    }
816
}
817
818
/// 3D CLUT helper
819
///
820
/// Represents hexahedron.
821
pub struct Cube<'a> {
822
    array: &'a [f32],
823
    x_stride: u32,
824
    y_stride: u32,
825
    grid_size: [u8; 3],
826
}
827
828
pub(crate) trait ArrayFetch<T> {
829
    fn fetch(&self, x: i32, y: i32, z: i32) -> T;
830
}
831
832
struct ArrayFetchVector3f<'a> {
833
    array: &'a [f32],
834
    x_stride: u32,
835
    y_stride: u32,
836
}
837
838
impl ArrayFetch<Vector3f> for ArrayFetchVector3f<'_> {
839
    #[inline(always)]
840
0
    fn fetch(&self, x: i32, y: i32, z: i32) -> Vector3f {
841
0
        let start = (x as u32 * self.x_stride + y as u32 * self.y_stride + z as u32) as usize * 3;
842
0
        let k = &self.array[start..start + 3];
843
0
        Vector3f {
844
0
            v: [k[0], k[1], k[2]],
845
0
        }
846
0
    }
847
}
848
849
struct ArrayFetchVector4f<'a> {
850
    array: &'a [f32],
851
    x_stride: u32,
852
    y_stride: u32,
853
}
854
855
impl ArrayFetch<Vector4f> for ArrayFetchVector4f<'_> {
856
    #[inline(always)]
857
0
    fn fetch(&self, x: i32, y: i32, z: i32) -> Vector4f {
858
0
        let start = (x as u32 * self.x_stride + y as u32 * self.y_stride + z as u32) as usize * 4;
859
0
        let k = &self.array[start..start + 4];
860
0
        Vector4f {
861
0
            v: [k[0], k[1], k[2], k[3]],
862
0
        }
863
0
    }
864
}
865
866
impl Cube<'_> {
867
0
    pub fn new(array: &[f32], grid_size: usize, channels: usize) -> Result<Cube<'_>, CmsError> {
868
0
        if array.is_empty() || grid_size == 0 {
869
0
            return Ok(Cube {
870
0
                array,
871
0
                x_stride: 0,
872
0
                y_stride: 0,
873
0
                grid_size: [0, 0, 0],
874
0
            });
875
0
        }
876
0
        let y_stride = grid_size;
877
0
        let x_stride = y_stride * y_stride;
878
879
0
        let last_index = (grid_size - 1)
880
0
            .safe_mul(x_stride)?
881
0
            .safe_add((grid_size - 1).safe_mul(y_stride)?)?
882
0
            .safe_add(grid_size - 1)?
883
0
            .safe_mul(channels)?;
884
885
0
        if last_index >= array.len() {
886
0
            return Err(CmsError::MalformedClut(MalformedSize {
887
0
                size: array.len(),
888
0
                expected: last_index,
889
0
            }));
890
0
        }
891
892
0
        Ok(Cube {
893
0
            array,
894
0
            x_stride: x_stride as u32,
895
0
            y_stride: y_stride as u32,
896
0
            grid_size: [grid_size as u8, grid_size as u8, grid_size as u8],
897
0
        })
898
0
    }
899
900
0
    pub fn new_cube(
901
0
        array: &[f32],
902
0
        grid_size: [u8; 3],
903
0
        channels: usize,
904
0
    ) -> Result<Cube<'_>, CmsError> {
905
0
        if array.is_empty() || grid_size[0] == 0 || grid_size[1] == 0 || grid_size[2] == 0 {
906
0
            return Ok(Cube {
907
0
                array,
908
0
                x_stride: 0,
909
0
                y_stride: 0,
910
0
                grid_size,
911
0
            });
912
0
        }
913
0
        let y_stride = grid_size[2] as u32;
914
0
        let x_stride = y_stride * grid_size[1] as u32;
915
0
        let last_index = (grid_size[0] as usize - 1)
916
0
            .safe_mul(x_stride as usize)?
917
0
            .safe_add((grid_size[1] as usize - 1).safe_mul(y_stride as usize)?)?
918
0
            .safe_add(grid_size[2] as usize - 1)?
919
0
            .safe_mul(channels)?;
920
921
0
        if last_index >= array.len() {
922
0
            return Err(CmsError::MalformedClut(MalformedSize {
923
0
                size: array.len(),
924
0
                expected: last_index,
925
0
            }));
926
0
        }
927
928
0
        Ok(Cube {
929
0
            array,
930
0
            x_stride,
931
0
            y_stride,
932
0
            grid_size,
933
0
        })
934
0
    }
935
936
    #[inline(always)]
937
0
    fn trilinear<
938
0
        T: Copy
939
0
            + From<f32>
940
0
            + Sub<T, Output = T>
941
0
            + Mul<T, Output = T>
942
0
            + Add<T, Output = T>
943
0
            + FusedMultiplyNegAdd<T>
944
0
            + FusedMultiplyAdd<T>,
945
0
    >(
946
0
        &self,
947
0
        lin_x: f32,
948
0
        lin_y: f32,
949
0
        lin_z: f32,
950
0
        fetch: impl ArrayFetch<T>,
951
0
    ) -> T {
952
0
        let lin_x = lin_x.max(0.0);
953
0
        let lin_y = lin_y.max(0.0);
954
0
        let lin_z = lin_z.max(0.0);
955
956
0
        let scale_x = (self.grid_size[0] as i32 - 1) as f32;
957
0
        let scale_y = (self.grid_size[1] as i32 - 1) as f32;
958
0
        let scale_z = (self.grid_size[2] as i32 - 1) as f32;
959
960
0
        let x = (lin_x * scale_x).floor().min(scale_x) as i32;
961
0
        let y = (lin_y * scale_y).floor().min(scale_y) as i32;
962
0
        let z = (lin_z * scale_z).floor().min(scale_z) as i32;
963
964
0
        let x_n = (lin_x * scale_x).ceil().min(scale_x) as i32;
965
0
        let y_n = (lin_y * scale_y).ceil().min(scale_y) as i32;
966
0
        let z_n = (lin_z * scale_z).ceil().min(scale_z) as i32;
967
968
0
        let x_d = T::from(lin_x * scale_x - x as f32);
969
0
        let y_d = T::from(lin_y * scale_y - y as f32);
970
0
        let z_d = T::from(lin_z * scale_z - z as f32);
971
972
0
        let c000 = fetch.fetch(x, y, z);
973
0
        let c100 = fetch.fetch(x_n, y, z);
974
0
        let c010 = fetch.fetch(x, y_n, z);
975
0
        let c110 = fetch.fetch(x_n, y_n, z);
976
0
        let c001 = fetch.fetch(x, y, z_n);
977
0
        let c101 = fetch.fetch(x_n, y, z_n);
978
0
        let c011 = fetch.fetch(x, y_n, z_n);
979
0
        let c111 = fetch.fetch(x_n, y_n, z_n);
980
981
0
        let c00 = c000.neg_mla(c000, x_d).mla(c100, x_d);
982
0
        let c10 = c010.neg_mla(c010, x_d).mla(c110, x_d);
983
0
        let c01 = c001.neg_mla(c001, x_d).mla(c101, x_d);
984
0
        let c11 = c011.neg_mla(c011, x_d).mla(c111, x_d);
985
986
0
        let c0 = c00.neg_mla(c00, y_d).mla(c10, y_d);
987
0
        let c1 = c01.neg_mla(c01, y_d).mla(c11, y_d);
988
989
0
        c0.neg_mla(c0, z_d).mla(c1, z_d)
990
0
    }
Unexecuted instantiation: <moxcms::nd_array::Cube>::trilinear::<moxcms::matrix::Vector3<f32>, moxcms::nd_array::ArrayFetchVector3f>
Unexecuted instantiation: <moxcms::nd_array::Cube>::trilinear::<moxcms::matrix::Vector4<f32>, moxcms::nd_array::ArrayFetchVector4f>
991
992
    #[cfg(feature = "options")]
993
    #[inline]
994
    fn pyramid<
995
        T: Copy
996
            + From<f32>
997
            + Sub<T, Output = T>
998
            + Mul<T, Output = T>
999
            + Add<T, Output = T>
1000
            + FusedMultiplyAdd<T>,
1001
    >(
1002
        &self,
1003
        lin_x: f32,
1004
        lin_y: f32,
1005
        lin_z: f32,
1006
        fetch: impl ArrayFetch<T>,
1007
    ) -> T {
1008
        let lin_x = lin_x.max(0.0);
1009
        let lin_y = lin_y.max(0.0);
1010
        let lin_z = lin_z.max(0.0);
1011
1012
        let scale_x = (self.grid_size[0] as i32 - 1) as f32;
1013
        let scale_y = (self.grid_size[1] as i32 - 1) as f32;
1014
        let scale_z = (self.grid_size[2] as i32 - 1) as f32;
1015
1016
        let x = (lin_x * scale_x).floor().min(scale_x) as i32;
1017
        let y = (lin_y * scale_y).floor().min(scale_y) as i32;
1018
        let z = (lin_z * scale_z).floor().min(scale_z) as i32;
1019
1020
        let x_n = (lin_x * scale_x).ceil().min(scale_x) as i32;
1021
        let y_n = (lin_y * scale_y).ceil().min(scale_y) as i32;
1022
        let z_n = (lin_z * scale_z).ceil().min(scale_z) as i32;
1023
1024
        let dr = lin_x * scale_x - x as f32;
1025
        let dg = lin_y * scale_y - y as f32;
1026
        let db = lin_z * scale_z - z as f32;
1027
1028
        let c0 = fetch.fetch(x, y, z);
1029
1030
        if dr > db && dg > db {
1031
            let x0 = fetch.fetch(x_n, y_n, z_n);
1032
            let x1 = fetch.fetch(x_n, y_n, z);
1033
            let x2 = fetch.fetch(x_n, y, z);
1034
            let x3 = fetch.fetch(x, y_n, z);
1035
1036
            let c1 = x0 - x1;
1037
            let c2 = x2 - c0;
1038
            let c3 = x3 - c0;
1039
            let c4 = c0 - x3 - x2 + x1;
1040
1041
            let s0 = c0.mla(c1, T::from(db));
1042
            let s1 = s0.mla(c2, T::from(dr));
1043
            let s2 = s1.mla(c3, T::from(dg));
1044
            s2.mla(c4, T::from(dr * dg))
1045
        } else if db > dr && dg > dr {
1046
            let x0 = fetch.fetch(x, y, z_n);
1047
            let x1 = fetch.fetch(x_n, y_n, z_n);
1048
            let x2 = fetch.fetch(x, y_n, z_n);
1049
            let x3 = fetch.fetch(x, y_n, z);
1050
1051
            let c1 = x0 - c0;
1052
            let c2 = x1 - x2;
1053
            let c3 = x3 - c0;
1054
            let c4 = c0 - x3 - x0 + x2;
1055
1056
            let s0 = c0.mla(c1, T::from(db));
1057
            let s1 = s0.mla(c2, T::from(dr));
1058
            let s2 = s1.mla(c3, T::from(dg));
1059
            s2.mla(c4, T::from(dg * db))
1060
        } else {
1061
            let x0 = fetch.fetch(x, y, z_n);
1062
            let x1 = fetch.fetch(x_n, y, z);
1063
            let x2 = fetch.fetch(x_n, y, z_n);
1064
            let x3 = fetch.fetch(x_n, y_n, z_n);
1065
1066
            let c1 = x0 - c0;
1067
            let c2 = x1 - c0;
1068
            let c3 = x3 - x2;
1069
            let c4 = c0 - x1 - x0 + x2;
1070
1071
            let s0 = c0.mla(c1, T::from(db));
1072
            let s1 = s0.mla(c2, T::from(dr));
1073
            let s2 = s1.mla(c3, T::from(dg));
1074
            s2.mla(c4, T::from(db * dr))
1075
        }
1076
    }
1077
1078
    #[cfg(feature = "options")]
1079
    #[inline]
1080
    fn tetra<
1081
        T: Copy
1082
            + From<f32>
1083
            + Sub<T, Output = T>
1084
            + Mul<T, Output = T>
1085
            + Add<T, Output = T>
1086
            + FusedMultiplyAdd<T>,
1087
    >(
1088
        &self,
1089
        lin_x: f32,
1090
        lin_y: f32,
1091
        lin_z: f32,
1092
        fetch: impl ArrayFetch<T>,
1093
    ) -> T {
1094
        let lin_x = lin_x.max(0.0);
1095
        let lin_y = lin_y.max(0.0);
1096
        let lin_z = lin_z.max(0.0);
1097
1098
        let scale_x = (self.grid_size[0] as i32 - 1) as f32;
1099
        let scale_y = (self.grid_size[1] as i32 - 1) as f32;
1100
        let scale_z = (self.grid_size[2] as i32 - 1) as f32;
1101
1102
        let x = (lin_x * scale_x).floor().min(scale_x) as i32;
1103
        let y = (lin_y * scale_y).floor().min(scale_y) as i32;
1104
        let z = (lin_z * scale_z).floor().min(scale_z) as i32;
1105
1106
        let x_n = (lin_x * scale_x).ceil().min(scale_x) as i32;
1107
        let y_n = (lin_y * scale_y).ceil().min(scale_y) as i32;
1108
        let z_n = (lin_z * scale_z).ceil().min(scale_z) as i32;
1109
1110
        let rx = lin_x * scale_x - x as f32;
1111
        let ry = lin_y * scale_y - y as f32;
1112
        let rz = lin_z * scale_z - z as f32;
1113
1114
        let c0 = fetch.fetch(x, y, z);
1115
        let c2;
1116
        let c1;
1117
        let c3;
1118
        if rx >= ry {
1119
            if ry >= rz {
1120
                //rx >= ry && ry >= rz
1121
                c1 = fetch.fetch(x_n, y, z) - c0;
1122
                c2 = fetch.fetch(x_n, y_n, z) - fetch.fetch(x_n, y, z);
1123
                c3 = fetch.fetch(x_n, y_n, z_n) - fetch.fetch(x_n, y_n, z);
1124
            } else if rx >= rz {
1125
                //rx >= rz && rz >= ry
1126
                c1 = fetch.fetch(x_n, y, z) - c0;
1127
                c2 = fetch.fetch(x_n, y_n, z_n) - fetch.fetch(x_n, y, z_n);
1128
                c3 = fetch.fetch(x_n, y, z_n) - fetch.fetch(x_n, y, z);
1129
            } else {
1130
                //rz > rx && rx >= ry
1131
                c1 = fetch.fetch(x_n, y, z_n) - fetch.fetch(x, y, z_n);
1132
                c2 = fetch.fetch(x_n, y_n, z_n) - fetch.fetch(x_n, y, z_n);
1133
                c3 = fetch.fetch(x, y, z_n) - c0;
1134
            }
1135
        } else if rx >= rz {
1136
            //ry > rx && rx >= rz
1137
            c1 = fetch.fetch(x_n, y_n, z) - fetch.fetch(x, y_n, z);
1138
            c2 = fetch.fetch(x, y_n, z) - c0;
1139
            c3 = fetch.fetch(x_n, y_n, z_n) - fetch.fetch(x_n, y_n, z);
1140
        } else if ry >= rz {
1141
            //ry >= rz && rz > rx
1142
            c1 = fetch.fetch(x_n, y_n, z_n) - fetch.fetch(x, y_n, z_n);
1143
            c2 = fetch.fetch(x, y_n, z) - c0;
1144
            c3 = fetch.fetch(x, y_n, z_n) - fetch.fetch(x, y_n, z);
1145
        } else {
1146
            //rz > ry && ry > rx
1147
            c1 = fetch.fetch(x_n, y_n, z_n) - fetch.fetch(x, y_n, z_n);
1148
            c2 = fetch.fetch(x, y_n, z_n) - fetch.fetch(x, y, z_n);
1149
            c3 = fetch.fetch(x, y, z_n) - c0;
1150
        }
1151
        let s0 = c0.mla(c1, T::from(rx));
1152
        let s1 = s0.mla(c2, T::from(ry));
1153
        s1.mla(c3, T::from(rz))
1154
    }
1155
1156
    #[cfg(feature = "options")]
1157
    #[inline]
1158
    fn prism<
1159
        T: Copy
1160
            + From<f32>
1161
            + Sub<T, Output = T>
1162
            + Mul<T, Output = T>
1163
            + Add<T, Output = T>
1164
            + FusedMultiplyAdd<T>,
1165
    >(
1166
        &self,
1167
        lin_x: f32,
1168
        lin_y: f32,
1169
        lin_z: f32,
1170
        fetch: impl ArrayFetch<T>,
1171
    ) -> T {
1172
        let lin_x = lin_x.max(0.0);
1173
        let lin_y = lin_y.max(0.0);
1174
        let lin_z = lin_z.max(0.0);
1175
1176
        let scale_x = (self.grid_size[0] as i32 - 1) as f32;
1177
        let scale_y = (self.grid_size[1] as i32 - 1) as f32;
1178
        let scale_z = (self.grid_size[2] as i32 - 1) as f32;
1179
1180
        let x = (lin_x * scale_x).floor().min(scale_x) as i32;
1181
        let y = (lin_y * scale_y).floor().min(scale_y) as i32;
1182
        let z = (lin_z * scale_z).floor().min(scale_z) as i32;
1183
1184
        let x_n = (lin_x * scale_x).ceil().min(scale_x) as i32;
1185
        let y_n = (lin_y * scale_y).ceil().min(scale_y) as i32;
1186
        let z_n = (lin_z * scale_z).ceil().min(scale_z) as i32;
1187
1188
        let dr = lin_x * scale_x - x as f32;
1189
        let dg = lin_y * scale_y - y as f32;
1190
        let db = lin_z * scale_z - z as f32;
1191
1192
        let c0 = fetch.fetch(x, y, z);
1193
1194
        if db >= dr {
1195
            let x0 = fetch.fetch(x, y, z_n);
1196
            let x1 = fetch.fetch(x_n, y, z_n);
1197
            let x2 = fetch.fetch(x, y_n, z);
1198
            let x3 = fetch.fetch(x, y_n, z_n);
1199
            let x4 = fetch.fetch(x_n, y_n, z_n);
1200
1201
            let c1 = x0 - c0;
1202
            let c2 = x1 - x0;
1203
            let c3 = x2 - c0;
1204
            let c4 = c0 - x2 - x0 + x3;
1205
            let c5 = x0 - x3 - x1 + x4;
1206
1207
            let s0 = c0.mla(c1, T::from(db));
1208
            let s1 = s0.mla(c2, T::from(dr));
1209
            let s2 = s1.mla(c3, T::from(dg));
1210
            let s3 = s2.mla(c4, T::from(dg * db));
1211
            s3.mla(c5, T::from(dr * dg))
1212
        } else {
1213
            let x0 = fetch.fetch(x_n, y, z);
1214
            let x1 = fetch.fetch(x_n, y, z_n);
1215
            let x2 = fetch.fetch(x, y_n, z);
1216
            let x3 = fetch.fetch(x_n, y_n, z);
1217
            let x4 = fetch.fetch(x_n, y_n, z_n);
1218
1219
            let c1 = x1 - x0;
1220
            let c2 = x0 - c0;
1221
            let c3 = x2 - c0;
1222
            let c4 = x0 - x3 - x1 + x4;
1223
            let c5 = c0 - x2 - x0 + x3;
1224
1225
            let s0 = c0.mla(c1, T::from(db));
1226
            let s1 = s0.mla(c2, T::from(dr));
1227
            let s2 = s1.mla(c3, T::from(dg));
1228
            let s3 = s2.mla(c4, T::from(dg * db));
1229
            s3.mla(c5, T::from(dr * dg))
1230
        }
1231
    }
1232
1233
0
    pub fn trilinear_vec3(&self, lin_x: f32, lin_y: f32, lin_z: f32) -> Vector3f {
1234
0
        self.trilinear(
1235
0
            lin_x,
1236
0
            lin_y,
1237
0
            lin_z,
1238
0
            ArrayFetchVector3f {
1239
0
                array: self.array,
1240
0
                x_stride: self.x_stride,
1241
0
                y_stride: self.y_stride,
1242
0
            },
1243
        )
1244
0
    }
1245
1246
    #[cfg(feature = "options")]
1247
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
1248
    pub fn prism_vec3(&self, lin_x: f32, lin_y: f32, lin_z: f32) -> Vector3f {
1249
        self.prism(
1250
            lin_x,
1251
            lin_y,
1252
            lin_z,
1253
            ArrayFetchVector3f {
1254
                array: self.array,
1255
                x_stride: self.x_stride,
1256
                y_stride: self.y_stride,
1257
            },
1258
        )
1259
    }
1260
1261
    #[cfg(feature = "options")]
1262
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
1263
    pub fn pyramid_vec3(&self, lin_x: f32, lin_y: f32, lin_z: f32) -> Vector3f {
1264
        self.pyramid(
1265
            lin_x,
1266
            lin_y,
1267
            lin_z,
1268
            ArrayFetchVector3f {
1269
                array: self.array,
1270
                x_stride: self.x_stride,
1271
                y_stride: self.y_stride,
1272
            },
1273
        )
1274
    }
1275
1276
    #[cfg(feature = "options")]
1277
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
1278
    pub fn tetra_vec3(&self, lin_x: f32, lin_y: f32, lin_z: f32) -> Vector3f {
1279
        self.tetra(
1280
            lin_x,
1281
            lin_y,
1282
            lin_z,
1283
            ArrayFetchVector3f {
1284
                array: self.array,
1285
                x_stride: self.x_stride,
1286
                y_stride: self.y_stride,
1287
            },
1288
        )
1289
    }
1290
1291
0
    pub fn trilinear_vec4(&self, lin_x: f32, lin_y: f32, lin_z: f32) -> Vector4f {
1292
0
        self.trilinear(
1293
0
            lin_x,
1294
0
            lin_y,
1295
0
            lin_z,
1296
0
            ArrayFetchVector4f {
1297
0
                array: self.array,
1298
0
                x_stride: self.x_stride,
1299
0
                y_stride: self.y_stride,
1300
0
            },
1301
        )
1302
0
    }
1303
1304
    #[cfg(feature = "options")]
1305
    pub fn tetra_vec4(&self, lin_x: f32, lin_y: f32, lin_z: f32) -> Vector4f {
1306
        self.tetra(
1307
            lin_x,
1308
            lin_y,
1309
            lin_z,
1310
            ArrayFetchVector4f {
1311
                array: self.array,
1312
                x_stride: self.x_stride,
1313
                y_stride: self.y_stride,
1314
            },
1315
        )
1316
    }
1317
1318
    #[cfg(feature = "options")]
1319
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
1320
    pub fn pyramid_vec4(&self, lin_x: f32, lin_y: f32, lin_z: f32) -> Vector4f {
1321
        self.pyramid(
1322
            lin_x,
1323
            lin_y,
1324
            lin_z,
1325
            ArrayFetchVector4f {
1326
                array: self.array,
1327
                x_stride: self.x_stride,
1328
                y_stride: self.y_stride,
1329
            },
1330
        )
1331
    }
1332
1333
    #[cfg(feature = "options")]
1334
    #[cfg_attr(docsrs, doc(cfg(feature = "options")))]
1335
    pub fn prism_vec4(&self, lin_x: f32, lin_y: f32, lin_z: f32) -> Vector4f {
1336
        self.prism(
1337
            lin_x,
1338
            lin_y,
1339
            lin_z,
1340
            ArrayFetchVector4f {
1341
                array: self.array,
1342
                x_stride: self.x_stride,
1343
                y_stride: self.y_stride,
1344
            },
1345
        )
1346
    }
1347
}