1pub const FP_SHIFT: u32 = 16;
24
25pub const FP_ONE: i32 = 1_i32 << FP_SHIFT;
27
28pub const Q16_MAX: i32 = i32::MAX;
30
31pub const Q16_MIN: i32 = i32::MIN;
33
34pub type Q16 = i32;
38
39#[inline(always)]
45pub fn to_q16(v: f32) -> Q16 {
46 (v * 65536.0_f32 + if v >= 0.0 { 0.5 } else { -0.5 }) as i32
47}
48
49#[inline(always)]
51pub fn from_q16(v: Q16) -> f32 {
52 v as f32 / 65536.0_f32
53}
54
55#[inline(always)]
57pub fn from_i16_q16(v: i16) -> Q16 {
58 (v as i32) << 16
59}
60
61#[inline(always)]
63pub fn to_i16_q16(v: Q16) -> i16 {
64 (v >> 16) as i16
65}
66
67#[inline(always)]
69pub fn q31_to_q16(v: i32) -> Q16 {
70 v >> 15
71}
72
73#[inline(always)]
75pub fn q16_to_q31(v: Q16) -> i64 {
76 (v as i64) << 16
77}
78
79#[inline(always)]
85pub fn mul_q16(a: Q16, b: Q16) -> Q16 {
86 ((a as i64 * b as i64) >> 16) as i32
87}
88
89#[inline(always)]
91pub fn mul_n_q16(a: Q16, n: i32) -> Q16 {
92 a.wrapping_mul(n)
93}
94
95#[inline(always)]
97pub fn mul_f_q16(a: Q16, f: f32) -> Q16 {
98 mul_q16(a, to_q16(f))
99}
100
101#[inline(always)]
103pub fn div_q16(a: Q16, b: Q16) -> Q16 {
104 if b == 0 {
105 return 0;
106 }
107 (((a as i64) << 16) / b as i64) as i32
108}
109
110#[inline(always)]
112pub fn div_n_q16(a: Q16, n: i32) -> Q16 {
113 if n == 0 {
114 return 0;
115 }
116 a / n
117}
118
119#[inline(always)]
121pub fn div_f_q16(a: Q16, f: f32) -> Q16 {
122 div_q16(a, to_q16(f))
123}
124
125#[inline(always)]
131pub fn qadd_q16(a: Q16, b: Q16) -> Q16 {
132 (a as i64 + b as i64).clamp(Q16_MIN as i64, Q16_MAX as i64) as i32
133}
134
135#[inline(always)]
137pub fn qsub_q16(a: Q16, b: Q16) -> Q16 {
138 (a as i64 - b as i64).clamp(Q16_MIN as i64, Q16_MAX as i64) as i32
139}
140
141#[inline(always)]
143pub fn abs_q16(a: Q16) -> Q16 {
144 a.abs()
145}
146
147#[inline(always)]
156pub fn lerp_q16(a: Q16, b: Q16, t: i32, denom: i32) -> Q16 {
157 if denom == 0 {
158 return a;
159 }
160 let diff = b as i64 - a as i64;
161 (a as i64 + diff * t as i64 / denom as i64) as i32
162}
163
164#[inline(always)]
166pub fn angle_to_q16(degrees: f32) -> Q16 {
167 to_q16(degrees * core::f32::consts::PI / 180.0)
168}
169
170#[inline(always)]
175pub fn recip_q16(v: Q16) -> Q16 {
176 if v == 0 {
177 return Q16_MAX;
178 }
179 ((1i64 << 32) / v as i64) as i32
180}
181
182#[derive(Debug, Clone, Copy)]
218pub struct ScanlineInterp {
219 z_cur: u32,
220 z_step: i32,
221 u_cur: u32,
222 u_step: i32,
223 v_cur: u32,
224 v_step: i32,
225}
226
227impl ScanlineInterp {
228 #[inline]
236 pub fn new(
237 z_left: u32,
238 z_right: u32,
239 u_left: u32,
240 u_right: u32,
241 v_left: u32,
242 v_right: u32,
243 span: i32,
244 ) -> Self {
245 let (z_step, u_step, v_step) = if span > 0 {
246 let z_step = ((z_right as i64 - z_left as i64) / span as i64) as i32;
247 let u_step = ((u_right as i64 - u_left as i64) / span as i64) as i32;
248 let v_step = ((v_right as i64 - v_left as i64) / span as i64) as i32;
249 (z_step, u_step, v_step)
250 } else {
251 (0, 0, 0)
252 };
253 Self {
254 z_cur: z_left,
255 z_step,
256 u_cur: u_left,
257 u_step,
258 v_cur: v_left,
259 v_step,
260 }
261 }
262
263 #[inline]
265 pub fn depth_only(z_left: u32, z_right: u32, span: i32) -> Self {
266 Self::new(z_left, z_right, 0, 0, 0, 0, span)
267 }
268
269 #[inline(always)]
271 pub fn z(&self) -> u32 {
272 self.z_cur
273 }
274
275 #[inline(always)]
277 pub fn u(&self) -> u32 {
278 self.u_cur
279 }
280
281 #[inline(always)]
283 pub fn v(&self) -> u32 {
284 self.v_cur
285 }
286
287 #[inline(always)]
289 pub fn step(&mut self) {
290 self.z_cur = self.z_cur.wrapping_add_signed(self.z_step);
291 self.u_cur = self.u_cur.wrapping_add_signed(self.u_step);
292 self.v_cur = self.v_cur.wrapping_add_signed(self.v_step);
293 }
294
295 #[inline]
297 pub fn step_n(&mut self, n: i32) {
298 self.z_cur = self.z_cur.wrapping_add_signed(self.z_step.wrapping_mul(n));
299 self.u_cur = self.u_cur.wrapping_add_signed(self.u_step.wrapping_mul(n));
300 self.v_cur = self.v_cur.wrapping_add_signed(self.v_step.wrapping_mul(n));
301 }
302
303 #[inline(always)]
305 pub fn z_f32(&self) -> f32 {
306 self.z_cur as f32 / 65536.0
307 }
308}
309
310#[cfg(test)]
315mod tests {
316 extern crate std;
317 use super::*;
318
319 #[test]
320 fn roundtrip_f32_q16() {
321 let values = [0.0f32, 0.25, -0.5, 1.0, 3.14159, -100.0, 32767.0];
322 for v in values {
323 let q = to_q16(v);
324 let back = from_q16(q);
325 let lsb = 1.0 / 65536.0_f32;
326 assert!(
327 (back - v).abs() <= lsb,
328 "roundtrip failed for {v}: got {back}"
329 );
330 }
331 }
332
333 #[test]
334 fn roundtrip_i16_q16() {
335 for v in [-32768i16, -1, 0, 1, 100, 32767] {
336 let q = from_i16_q16(v);
337 let back = to_i16_q16(q);
338 assert_eq!(back, v, "i16 roundtrip failed for {v}");
339 }
340 }
341
342 #[test]
343 fn q31_q16_shifts() {
344 let q31_one = 0x7FFF_FFFFi32;
345 let q16 = q31_to_q16(q31_one);
346 let f = from_q16(q16);
347 assert!(
348 (f - 1.0).abs() < 1e-4,
349 "Q31->Q16 should be near 1.0, got {f}"
350 );
351 }
352
353 #[test]
354 fn mul_q16_basic() {
355 let a = to_q16(1.5);
356 let b = to_q16(2.0);
357 let result = from_q16(mul_q16(a, b));
358 assert!((result - 3.0).abs() < 1e-4, "1.5 * 2.0 = {result}");
359 }
360
361 #[test]
362 fn mul_q16_negative() {
363 let a = to_q16(-2.5);
364 let b = to_q16(4.0);
365 let result = from_q16(mul_q16(a, b));
366 assert!((result - (-10.0)).abs() < 1e-4, "-2.5 * 4.0 = {result}");
367 }
368
369 #[test]
370 fn mul_n_q16_integer_scale() {
371 let a = to_q16(3.5);
372 let result = from_q16(mul_n_q16(a, 4));
373 assert!((result - 14.0).abs() < 1e-4, "3.5 * 4 = {result}");
374 }
375
376 #[test]
377 fn mul_f_q16_float_scale() {
378 let a = to_q16(2.0);
379 let result = from_q16(mul_f_q16(a, 1.5));
380 assert!((result - 3.0).abs() < 1e-3, "2.0 * 1.5f = {result}");
381 }
382
383 #[test]
384 fn div_q16_basic() {
385 let result = from_q16(div_q16(to_q16(3.0), to_q16(2.0)));
386 assert!((result - 1.5).abs() < 1e-4, "3.0 / 2.0 = {result}");
387 }
388
389 #[test]
390 fn div_q16_zero_denominator() {
391 let result = div_q16(to_q16(5.0), 0);
392 assert_eq!(result, 0, "division by zero must return 0");
393 }
394
395 #[test]
396 fn div_n_q16_integer_divisor() {
397 let result = from_q16(div_n_q16(to_q16(9.0), 3));
398 assert!((result - 3.0).abs() < 1e-4, "9.0 / 3 = {result}");
399 }
400
401 #[test]
402 fn div_f_q16_float_divisor() {
403 let result = from_q16(div_f_q16(to_q16(6.0), 2.0));
404 assert!((result - 3.0).abs() < 1e-3, "6.0 / 2.0f = {result}");
405 }
406
407 #[test]
408 fn qadd_q16_no_overflow() {
409 assert_eq!(
410 from_q16(qadd_q16(to_q16(1.0), to_q16(2.0))).round() as i32,
411 3
412 );
413 }
414
415 #[test]
416 fn qadd_q16_saturates_at_max() {
417 let result = qadd_q16(Q16_MAX, Q16_MAX);
418 assert_eq!(result, Q16_MAX, "overflow must saturate at Q16_MAX");
419 }
420
421 #[test]
422 fn qsub_q16_saturates_at_min() {
423 let result = qsub_q16(Q16_MIN, Q16_MAX);
424 assert_eq!(result, Q16_MIN, "underflow must saturate at Q16_MIN");
425 }
426
427 #[test]
428 fn abs_q16_positive_unchanged() {
429 let v = to_q16(5.75);
430 assert_eq!(abs_q16(v), v);
431 }
432
433 #[test]
434 fn abs_q16_negated() {
435 let v = to_q16(-3.14);
436 let expected = to_q16(3.14);
437 assert_eq!(abs_q16(v), expected);
438 }
439
440 #[test]
441 fn lerp_q16_midpoint() {
442 let a = to_q16(0.0);
443 let b = to_q16(1.0);
444 let mid = from_q16(lerp_q16(a, b, 1, 2));
445 assert!((mid - 0.5).abs() < 1e-4, "lerp mid = {mid}");
446 }
447
448 #[test]
449 fn lerp_q16_endpoints() {
450 let a = to_q16(10.0);
451 let b = to_q16(20.0);
452 assert_eq!(lerp_q16(a, b, 0, 10), a, "t=0 must return left endpoint");
453 assert_eq!(
454 lerp_q16(a, b, 10, 10),
455 b,
456 "t=denom must return right endpoint"
457 );
458 }
459
460 #[test]
461 fn lerp_q16_zero_denom_returns_left() {
462 let a = to_q16(5.0);
463 let b = to_q16(9.0);
464 assert_eq!(lerp_q16(a, b, 0, 0), a, "zero denom must return left");
465 }
466
467 #[test]
468 fn angle_to_q16_90_degrees() {
469 let q = angle_to_q16(90.0);
470 let rad = from_q16(q);
471 assert!(
472 (rad - core::f32::consts::FRAC_PI_2).abs() < 1e-4,
473 "90 deg should be pi/2, got {rad}"
474 );
475 }
476
477 #[test]
478 fn angle_to_q16_360_degrees() {
479 let q = angle_to_q16(360.0);
480 let rad = from_q16(q);
481 assert!(
482 (rad - 2.0 * core::f32::consts::PI).abs() < 1e-4,
483 "360 deg should be 2*pi, got {rad}"
484 );
485 }
486
487 #[test]
488 fn recip_q16_one() {
489 let result = from_q16(recip_q16(FP_ONE));
490 assert!(
491 (result - 1.0).abs() < 0.01,
492 "recip(1.0) approx 1.0, got {result}"
493 );
494 }
495
496 #[test]
497 fn recip_q16_two() {
498 let result = from_q16(recip_q16(to_q16(2.0)));
499 assert!(
500 (result - 0.5).abs() < 0.01,
501 "recip(2.0) approx 0.5, got {result}"
502 );
503 }
504
505 #[test]
506 fn recip_q16_zero_returns_sentinel() {
507 assert_eq!(
508 recip_q16(0),
509 Q16_MAX,
510 "recip(0) must return Q16_MAX sentinel"
511 );
512 }
513
514 #[test]
515 fn scanline_interp_starts_at_left() {
516 let interp = ScanlineInterp::new(100, 200, 0, 65536, 0, 32768, 10);
517 assert_eq!(interp.z(), 100);
518 assert_eq!(interp.u(), 0);
519 assert_eq!(interp.v(), 0);
520 }
521
522 #[test]
523 fn scanline_interp_step_reaches_right() {
524 let mut interp = ScanlineInterp::depth_only(0, 65536, 10);
525 for _ in 0..10 {
526 interp.step();
527 }
528 let diff = (interp.z() as i64 - 65536i64).abs();
529 assert!(
530 diff <= 10,
531 "z after 10 steps should be within 10 of 65536, got {} (diff={})",
532 interp.z(),
533 diff
534 );
535 }
536
537 #[test]
538 fn scanline_interp_uv_reaches_right() {
539 let mut interp = ScanlineInterp::new(0, 0, 0, 65536, 0, 65536, 8);
540 for _ in 0..8 {
541 interp.step();
542 }
543 let u_err = (interp.u() as i64 - 65536i64).abs();
544 let v_err = (interp.v() as i64 - 65536i64).abs();
545 assert!(
546 u_err <= 1,
547 "u after 8 steps should be 65536 +/- 1, got {}",
548 interp.u()
549 );
550 assert!(
551 v_err <= 1,
552 "v after 8 steps should be 65536 +/- 1, got {}",
553 interp.v()
554 );
555 }
556
557 #[test]
558 fn scanline_interp_zero_span() {
559 let mut interp = ScanlineInterp::new(1000, 9999, 0, 65536, 0, 65536, 0);
560 interp.step();
561 interp.step();
562 assert_eq!(interp.z(), 1000, "zero-span z must not move");
563 assert_eq!(interp.u(), 0, "zero-span u must not move");
564 }
565
566 #[test]
567 fn scanline_interp_step_n() {
568 let mut interp = ScanlineInterp::depth_only(0, 100000, 10);
569 interp.step_n(5);
570 let expected = 50000u32;
571 let diff = (interp.z() as i64 - expected as i64).abs();
572 assert!(
573 diff <= 1,
574 "step_n(5) should land at 50000 +/- 1, got {}",
575 interp.z()
576 );
577 }
578
579 #[test]
580 fn scanline_interp_z_f32() {
581 let interp = ScanlineInterp::depth_only(65536, 65536, 0);
582 assert!((interp.z_f32() - 1.0).abs() < 1e-5, "z_f32 should be 1.0");
583 }
584}