| ... | ... | @@ -428,6 +428,100 @@ pub const min3 = @compileError("deprecated; use @min instead"); |
| 428 | 428 | pub const max3 = @compileError("deprecated; use @max instead"); |
| 429 | 429 | pub const ln = @compileError("deprecated; use @log instead"); |
| 430 | 430 | |
| 431 | /// Odd sawtooth function |
| 432 | /// ``` |
| 433 | /// | |
| 434 | /// / | / / |
| 435 | /// / |/ / |
| 436 | /// --/----/----/-- |
| 437 | /// / /| / |
| 438 | /// / / | / |
| 439 | /// | |
| 440 | /// ``` |
| 441 | /// Limit x to the half-open interval [-r, r). |
| 442 | pub fn wrap(x: anytype, r: anytype) @TypeOf(x) { |
| 443 | const info_x = @typeInfo(@TypeOf(x)); |
| 444 | const info_r = @typeInfo(@TypeOf(r)); |
| 445 | if (info_x == .Int and info_x.Int.signedness != .signed) { |
| 446 | @compileError("x must be floating point, comptime integer, or signed integer."); |
| 447 | } |
| 448 | switch (info_r) { |
| 449 | .Int => { |
| 450 | // in the rare usecase of r not being comptime_int or float, |
| 451 | // take the penalty of having an intermediary type conversion, |
| 452 | // otherwise the alternative is to unwind iteratively to avoid overflow |
| 453 | const R = comptime do: { |
| 454 | var info = info_r; |
| 455 | info.Int.bits += 1; |
| 456 | info.Int.signedness = .signed; |
| 457 | break :do @Type(info); |
| 458 | }; |
| 459 | const radius: if (info_r.Int.signedness == .signed) @TypeOf(r) else R = r; |
| 460 | return @intCast(@mod(x - radius, 2 * @as(R, r)) - r); // provably impossible to overflow |
| 461 | }, |
| 462 | else => { |
| 463 | return @mod(x - r, 2 * r) - r; |
| 464 | }, |
| 465 | } |
| 466 | } |
| 467 | test "wrap" { |
| 468 | // Within range |
| 469 | try testing.expect(wrap(@as(i32, -75), @as(i32, 180)) == -75); |
| 470 | try testing.expect(wrap(@as(i32, -75), @as(i32, -180)) == -75); |
| 471 | // Below |
| 472 | try testing.expect(wrap(@as(i32, -225), @as(i32, 180)) == 135); |
| 473 | try testing.expect(wrap(@as(i32, -225), @as(i32, -180)) == 135); |
| 474 | // Above |
| 475 | try testing.expect(wrap(@as(i32, 361), @as(i32, 180)) == 1); |
| 476 | try testing.expect(wrap(@as(i32, 361), @as(i32, -180)) == 1); |
| 477 | |
| 478 | // One period, right limit, positive r |
| 479 | try testing.expect(wrap(@as(i32, 180), @as(i32, 180)) == -180); |
| 480 | // One period, left limit, positive r |
| 481 | try testing.expect(wrap(@as(i32, -180), @as(i32, 180)) == -180); |
| 482 | // One period, right limit, negative r |
| 483 | try testing.expect(wrap(@as(i32, 180), @as(i32, -180)) == 180); |
| 484 | // One period, left limit, negative r |
| 485 | try testing.expect(wrap(@as(i32, -180), @as(i32, -180)) == 180); |
| 486 | |
| 487 | // Two periods, right limit, positive r |
| 488 | try testing.expect(wrap(@as(i32, 540), @as(i32, 180)) == -180); |
| 489 | // Two periods, left limit, positive r |
| 490 | try testing.expect(wrap(@as(i32, -540), @as(i32, 180)) == -180); |
| 491 | // Two periods, right limit, negative r |
| 492 | try testing.expect(wrap(@as(i32, 540), @as(i32, -180)) == 180); |
| 493 | // Two periods, left limit, negative r |
| 494 | try testing.expect(wrap(@as(i32, -540), @as(i32, -180)) == 180); |
| 495 | |
| 496 | // Floating point |
| 497 | try testing.expect(wrap(@as(f32, 1.125), @as(f32, 1.0)) == -0.875); |
| 498 | try testing.expect(wrap(@as(f32, -127.5), @as(f32, 180)) == -127.5); |
| 499 | |
| 500 | // Mix of comptime and non-comptime |
| 501 | var i: i32 = 1; |
| 502 | _ = &i; |
| 503 | try testing.expect(wrap(i, 10) == 1); |
| 504 | } |
| 505 | test wrap { |
| 506 | const limit: i32 = 180; |
| 507 | // Within range |
| 508 | try testing.expect(wrap(@as(i32, -75), limit) == -75); |
| 509 | // Below |
| 510 | try testing.expect(wrap(@as(i32, -225), limit) == 135); |
| 511 | // Above |
| 512 | try testing.expect(wrap(@as(i32, 361), limit) == 1); |
| 513 | } |
| 514 | |
| 515 | /// Odd ramp function |
| 516 | /// ``` |
| 517 | /// | _____ |
| 518 | /// | / |
| 519 | /// |/ |
| 520 | /// -------/------- |
| 521 | /// /| |
| 522 | /// _____/ | |
| 523 | /// | |
| 524 | /// ``` |
| 431 | 525 | /// Limit val to the inclusive range [lower, upper]. |
| 432 | 526 | pub fn clamp(val: anytype, lower: anytype, upper: anytype) @TypeOf(val, lower, upper) { |
| 433 | 527 | assert(lower <= upper); |