Coverage Report

Created: 2025-12-11 07:11

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