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