@@ -15,7 +15,7 @@ The crate is `no_std` and supports `i16`, `i32`, and `i64` coordinate types.
1515
1616``` toml
1717[dependencies ]
18- i_float = " 4 .0"
18+ i_float = " 5 .0"
1919```
2020
2121The default ` core ` feature exposes the complete numeric and geometry API.
@@ -68,21 +68,73 @@ fit. Conversely, an `IntVector` constructed directly from arbitrary wide values
6868is not covered by the point-coordinate bound.
6969
7070Floating-point input should normally be mapped with ` FloatPointAdapter ` . For an
71- explicit general-purpose safety margin, use ` with_coordinate_bits ` with at most
72- ` I::BITS - 3 ` ; algorithms with stronger range analysis may select a larger bit
71+ explicit general-purpose safety margin, use the conservative constructors. Their
72+ ` CONSERVATIVE_COORDINATE_BITS = I::BITS - 3 ` budget reserves an extra bit for
73+ rounding within the arithmetic range; algorithms with stronger range analysis may select a larger bit
7374budget.
7475
76+ ### Fast normalization
77+
78+ ` IntVector::fast_normalize() ` returns an approximate ` UnitIntVector<T> ` , or
79+ ` None ` for a zero vector. It favors speed over precision: about 6, 14, or 30 bits
80+ of direction precision for ` i16 ` , ` i32 ` , or ` i64 ` , respectively.
81+ The length is at most one. Normalization uses ` sqr_length() ` under the same
82+ arithmetic-range contract as other vector operations on point differences.
83+ It shifts the squared length to retain fractional precision in the reciprocal,
84+ then applies that reciprocal to the original components.
85+
86+ ``` rust
87+ use i_float :: int :: vector :: IntVector ;
88+
89+ let direction = IntVector :: <i32 >:: new (3 , 4 ). fast_normalize (). unwrap ();
90+ let offset = direction * 10 ;
91+ assert_eq! (offset , IntVector :: <i32 >:: new (6 , 8 ));
92+ assert! (IntVector :: <i32 >:: new (0 , 0 ). fast_normalize (). is_none ());
93+ ```
94+
95+ ` UnitIntVector::x() ` and ` y() ` return stored integers with scale ` 2^14 ` , ` 2^30 ` ,
96+ or ` 2^62 ` for ` i16 ` , ` i32 ` , or ` i64 ` , respectively. Multiplication by a scalar
97+ of type ` T ` (or ` .scale(scalar) ` ) returns an ` IntVector<T> ` , rounding to the
98+ nearest integer with midpoint values away from zero. Approximation error in
99+ the direction grows with the magnitude of the scalar.
100+
101+ ## Floating-point coordinate range
102+
103+ Floating-point geometry supports finite input coordinates and rectangle bounds
104+ within these inclusive limits:
105+
106+ | Scalar | Maximum absolute coordinate |
107+ | --- | --- |
108+ | ` f32 ` | ` 2^60 ` (approximately ` 1.15e18 ` ) |
109+ | ` f64 ` | ` 2^500 ` (approximately ` 3.27e150 ` ) |
110+
111+ These limits leave headroom for point differences, their dot and cross products,
112+ squared lengths, and midpoints without overflow. They do not guarantee exact
113+ arithmetic: ordinary floating-point rounding, cancellation, and underflow still
114+ apply. Arbitrary scaling and repeated operations require their own range analysis.
115+
116+ ` FloatPoint::normalize ` and ` FloatPointMath::normalize ` additionally require a
117+ positive, finite, normal squared length: at least ` f32::MIN_POSITIVE ` or
118+ ` f64::MIN_POSITIVE ` . A nonzero vector alone is insufficient because squaring tiny
119+ components can underflow. Rescale such vectors before normalizing them.
120+
75121## Floating-point adapter
76122
77123` FloatPointAdapter ` maps a bounded floating-point coordinate space onto an
78124integer grid. The same adapter converts results back into the original space.
125+ Its input bounds must satisfy the floating-point coordinate range above and
126+ have ` min <= max ` on each axis. All adapter constructors validate bounds,
127+ including rectangles assembled through public fields. Fallible constructors
128+ return ` FloatPointAdapterScaleError::InvalidRect ` ; infallible constructors panic.
129+ Checked point conversions validate membership in the original rectangle or
130+ the enclosing integer grid.
79131
80132``` rust
81133use i_float :: adapter :: FloatPointAdapter ;
82134use i_float :: float :: rect :: FloatRect ;
83135use i_float :: int :: point :: IntPoint ;
84136
85- let bounds = FloatRect :: new (- 10.0_f64 , 10.0 , - 5.0 , 5.0 );
137+ let bounds = FloatRect :: new (- 10.0_f64 , 10.0 , - 5.0 , 5.0 ). unwrap () ;
86138let adapter = FloatPointAdapter :: <[f64 ; 2], i32 >:: new (bounds );
87139
88140let source = [2.5 , - 1.25 ];
@@ -94,12 +146,58 @@ assert!((restored[0] - source[0]).abs() <= tolerance);
94146assert! ((restored [1 ] - source [1 ]). abs () <= tolerance );
95147```
96148
149+ Use ` new_conservative(rect) ` or ` with_iter_conservative(iter) ` for automatic
150+ scaling with the conservative coordinate range. Use
151+ ` try_with_scale_conservative(rect, scale) ` or
152+ ` try_with_iter_and_scale_conservative(iter, scale) ` to validate an explicit scale
153+ against the same budget. The associated ` CONSERVATIVE_COORDINATE_BITS ` constant
154+ is ` I::BITS - 3 ` : converted coordinates stay within the inclusive range
155+ ` [-2^(I::BITS - 3), 2^(I::BITS - 3)] ` , leaving an extra bit for rounding inside
156+ the strict point arithmetic range.
157+
97158Use ` with_coordinate_bits ` when an algorithm has an explicit coordinate-bit
98159budget. The value controls only the converted coordinate magnitude; it does not
99160prove that every later arithmetic expression is safe. Use ` try_with_scale ` or
100161` try_with_scale_and_coordinate_bits ` when a caller supplies the scale and
101162invalid or unsafe scales must be rejected.
102163
164+ For input iterators, use ` with_iter_and_coordinate_bits ` or
165+ ` try_with_iter_and_scale_and_coordinate_bits ` . Both accept any ` IntNumber `
166+ implementation, so downstream algorithms can share these constructors while
167+ choosing their own bit budget:
168+
169+ ``` rust
170+ use i_float :: adapter :: FloatPointAdapter ;
171+
172+ let points = [[- 3.0_f64 , - 1.0 ], [3.0 , 1.0 ]];
173+ let adapter = FloatPointAdapter :: <[f64 ; 2], i32 >:: with_iter_and_coordinate_bits (
174+ points . iter (), i32 :: BITS - 3 ,
175+ );
176+ let fixed = FloatPointAdapter :: <[f64 ; 2], i32 >:: try_with_iter_and_scale_and_coordinate_bits (
177+ points . iter (), 100.0 , i32 :: BITS - 3 ,
178+ )? ;
179+ assert_eq! (fixed . dir_scale (), 100.0 );
180+ # Ok :: <(), i_float :: adapter :: FloatPointAdapterScaleError >(())
181+ ```
182+
183+ ` new ` and ` with_iter ` return the adapter directly and panic for invalid bounds
184+ or input points. An empty iterator uses zero bounds and scale one.
185+
186+ Automatic scales are capped at the largest finite power of two of the scalar
187+ type (` 2^127 ` for ` f32 ` , ` 2^1023 ` for ` f64 ` ), trading precision for finite scales
188+ on very small bounds. Within the supported coordinate range, automatic scales
189+ have finite reciprocals. Checked constructors preserve explicit scales and return
190+ ` ScaleTooSmall ` if the reciprocal is non-finite. ` with_scale ` panics for invalid
191+ scales. ` with_coordinate_bits ` panics for invalid bounds or a bit budget greater
192+ than ` I::BITS - 2 ` .
193+
194+ The adapter retains the original floating-point bounds for input validation.
195+ Integer-to-float conversion checks an internal ` IntRect ` enclosing those bounds
196+ on the selected grid (minimum rounded down, maximum rounded up). Snapping a valid
197+ input can therefore return a grid point just outside the original float bounds.
198+ ` rect() ` continues to return the original bounds; ` try_snap_to_grid ` rejects
199+ inputs outside them.
200+
103201## Fixed-scale ratios
104202
105203` UnitRatio<I> ` represents a value in the inclusive range ` 0..=1 ` . Its stored
@@ -126,6 +224,20 @@ stored value between zero and `DENOMINATOR`, `from_float` expects a finite value
126224between zero and one, and ` from_int ` expects ` 0 <= numerator <= denominator ` .
127225These preconditions are checked by debug assertions.
128226
227+ ## Integer rotations
228+
229+ ` int::angle::{Angle, Rotation} ` provides integer CORDIC angle measurement and
230+ reusable rotation matrices for ` UnitIntVector ` . Arc subdivision and storage stay
231+ in the consumer. See the [ API, accuracy results, and reproducible arc benchmark] ( docs/cordic.md ) .
232+ ` Rotation::<i32>::with_precision(angle, 4) ` selects fewer iterations with an angle/16
233+ error budget (3 gives angle/8). ` Rotation<I> ` stores coefficients in ` I ` , sharing
234+ the vector scale (Q14/Q30/Q62); i64 retains Q30 kernel precision. ` rotation.angle() `
235+ reports the achieved step for calculating counts and remainders.
236+ For runtime iteration/precision controls, run
237+ ` cargo run --release --example cordic_precision -- --sweep ` ;
238+ or use ` --relative 16 --step 20 ` to select rotation iterations from a relative
239+ angular tolerance. See the [ precision experiment] ( docs/cordic-precision.md ) .
240+
129241## Features
130242
131243| Feature | Default | Description |
0 commit comments