1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
//! Periodic parameters: how a parameter of a closed curve or surface is
//! wrapped, shifted and differenced. The period always comes from the
//! entity (`Curve::period`, `Surface::period`); nothing here assumes `2π`
//! except [`wrap_angle`], which says so (`docs/DATA-MODEL.md` §Geometry).
use crate::;
/// `t` moved by whole periods into `[lo, lo + period)`. A value already in
/// that range comes back unchanged, bit for bit (no add-and-subtract of
/// `lo`), and a result that rounds up to `lo + period` becomes `lo` — the
/// same point on the circle, and inside the range. A non-finite `t` comes
/// back unchanged.
///
/// ```
/// use arris_math::wrap_into;
///
/// assert_eq!(wrap_into(7.0, 2.0, 3.0), 4.0);
/// // Exactly at `lo + period` is the start of the next period.
/// assert_eq!(wrap_into(5.0, 2.0, 3.0), 2.0);
/// // A period that is not a turn.
/// assert_eq!(wrap_into(-0.5, 0.0, 1.5), 1.0);
/// // A tiny negative value whose sum with the period rounds up to it.
/// assert_eq!(wrap_into(-1e-300, 0.0, 3.0), 0.0);
/// ```
/// An angle moved into `[0, 2π)`: what a periodic curve's or surface's
/// parameter is reported in (`docs/DATA-MODEL.md` §Conventions). A
/// negative angle whose sum with `2π` rounds up to `2π` becomes `0` —
/// the same point on the circle, and inside the domain. A non-finite
/// angle comes back unchanged.
///
/// ```
/// use arris_math::wrap_angle;
/// use core::f64::consts::TAU;
///
/// assert_eq!(wrap_angle(0.0), 0.0);
/// assert_eq!(wrap_angle(-1.0), TAU - 1.0);
/// assert_eq!(wrap_angle(-1e-300), 0.0);
/// assert_eq!(wrap_angle(TAU + 1.0), 1.0);
/// ```
/// A difference moved into `[−period / 2, period / 2)`, by wrapping
/// `x + period / 2` into `[0, period)` and moving back. Unlike
/// [`wrap_signed`] it can return a value one rounding from `x` when `x`
/// is already in range, because of the add and subtract of the half
/// period; callers that were written against it keep that rounding.
///
/// ```
/// use arris_math::wrap_offset;
/// use core::f64::consts::{PI, TAU};
///
/// assert_eq!(wrap_offset(0.0, TAU), 0.0);
/// // Exactly half a period goes to the lower end.
/// assert_eq!(wrap_offset(PI, TAU), -PI);
/// assert!((wrap_offset(TAU + 0.25, TAU) - 0.25).abs() < 1e-15);
/// // A period that is not a turn.
/// assert_eq!(wrap_offset(1.0, 1.0), 0.0);
/// ```
/// The whole-period shift that moves `x` nearest to `target`: `k·period`
/// with `k` the integer nearest `(target − x) / period` (halves away from
/// zero). Adding it to `x` is the translate of `x` that lies closest to
/// `target`; it is `0.0` when `x` already is.
///
/// ```
/// use arris_math::shift_nearest;
/// use core::f64::consts::TAU;
///
/// assert_eq!(shift_nearest(0.5, 0.5 + TAU, TAU), TAU);
/// assert_eq!(shift_nearest(0.5, 0.5 - 2.0 * TAU, TAU), -2.0 * TAU);
/// assert_eq!(shift_nearest(1.0, 1.4, TAU), 0.0);
/// // A period that is not a turn.
/// assert_eq!(shift_nearest(0.0, 7.0, 3.0), 6.0);
/// ```
/// `d` rounded to whole periods in each periodic parameter of a surface
/// (`Surface::period`), zero in the others: the translate in (u, v) that
/// carries a point by `d` back to the nearest copy of itself.
///
/// ```
/// use arris_math::{Vec2, shift_nearest_uv};
/// use core::f64::consts::TAU;
///
/// let by = shift_nearest_uv(Vec2::new(TAU + 0.1, 5.0), [Some(TAU), None]);
/// assert_eq!(by, Vec2::new(TAU, 0.0));
/// ```
/// A difference moved into `[−period / 2, period / 2]`: `d` less the
/// whole periods nearest it. A half period goes to whichever end the
/// rounding of [`shift_nearest`] sends it, so the ends are not a promise.
///
/// ```
/// use arris_math::wrap_signed;
/// use core::f64::consts::TAU;
///
/// assert_eq!(wrap_signed(0.5, TAU), 0.5);
/// assert!((wrap_signed(TAU - 0.5, TAU) + 0.5).abs() < 1e-15);
/// assert_eq!(wrap_signed(-4.0, 3.0), -1.0);
/// ```
/// `t` moved by whole periods into `range` when it can be; a parameter of
/// a non-periodic entity (`period` of `None`) only if it already lies in
/// `range`. The shift is the smallest one that brings `t` to or above
/// `range.lo()`, so the answer is the first translate at or after the
/// start, and `None` when that overshoots `range.hi()`.
///
/// ```
/// use arris_math::{Interval, shift_into_range};
///
/// let range = Interval::new(1.0, 2.0).unwrap();
/// assert_eq!(shift_into_range(range, 1.5 + 6.0, Some(3.0)), Some(1.5));
/// assert_eq!(shift_into_range(range, 0.5, Some(3.0)), None);
/// assert_eq!(shift_into_range(range, 1.5, None), Some(1.5));
/// assert_eq!(shift_into_range(range, 2.5, None), None);
/// ```
/// The end of one whole period from `lo`: `lo + period`, stepped down to
/// the representable value below when that sum rounds up, so that
/// `end - lo <= period` holds exactly. A closed edge spans one period and
/// no more (`docs/DATA-MODEL.md` §Invariants, E1), and for a
/// `lo` that is not a small multiple of the period the sum can round to
/// one unit in the last place too far; this is the range's construction,
/// not a tolerance. A non-finite argument, or a `period` that is not
/// positive, comes back as `lo + period`.
///
/// ```
/// use arris_math::period_end;
/// use core::f64::consts::TAU;
///
/// assert_eq!(period_end(0.0, TAU), TAU);
/// // A pave one unit in the last place below a full turn: the sum
/// // rounds up, and the end is stepped back to keep the turn one turn.
/// let lo = f64::from_bits(TAU.to_bits() - 1);
/// assert!(lo + TAU - lo > TAU);
/// assert!(period_end(lo, TAU) - lo <= TAU);
/// ```