Skip to content

Commit 1f3c41e

Browse files
Merge pull request #8 from iShape-Rust/feature/unit_vector
Feature/unit vector
2 parents 1ff6354 + be6306f commit 1f3c41e

50 files changed

Lines changed: 5482 additions & 288 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/ci.yml‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,8 @@ jobs:
6565
steps:
6666
- uses: actions/checkout@v4
6767

68-
- name: Check feature configuration
69-
run: cargo check ${{ matrix.args }}
68+
- name: Test feature configuration
69+
run: cargo test ${{ matrix.args }}
7070

7171
msrv:
7272
name: MSRV / Rust 1.85

‎Cargo.toml‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "i_float"
3-
version = "4.1.0"
3+
version = "5.0.0"
44
authors = ["Nail Sharipov <nailxsharipov@gmail.com>"]
55
edition = "2024"
66
rust-version = "1.85"
@@ -20,3 +20,11 @@ serde = ["dep:serde", "core"]
2020
serde = { version = "^1.0", default-features = false, features = ["derive"], optional = true }
2121
glam = { optional = true, version = ">=0.27" }
2222
libm = "^0.2"
23+
24+
[[example]]
25+
name = "cordic_arc_bench"
26+
required-features = ["core"]
27+
28+
[[example]]
29+
name = "cordic_precision"
30+
required-features = ["core"]

‎README.md‎

Lines changed: 116 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

2121
The default `core` feature exposes the complete numeric and geometry API.
@@ -68,21 +68,73 @@ fit. Conversely, an `IntVector` constructed directly from arbitrary wide values
6868
is not covered by the point-coordinate bound.
6969

7070
Floating-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
7374
budget.
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
78124
integer 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
81133
use i_float::adapter::FloatPointAdapter;
82134
use i_float::float::rect::FloatRect;
83135
use 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();
86138
let adapter = FloatPointAdapter::<[f64; 2], i32>::new(bounds);
87139

88140
let source = [2.5, -1.25];
@@ -94,12 +146,58 @@ assert!((restored[0] - source[0]).abs() <= tolerance);
94146
assert!((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+
97158
Use `with_coordinate_bits` when an algorithm has an explicit coordinate-bit
98159
budget. The value controls only the converted coordinate magnitude; it does not
99160
prove 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
101162
invalid 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
126224
between zero and one, and `from_int` expects `0 <= numerator <= denominator`.
127225
These 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

Comments
 (0)