# 📦 WP-055 — `feat(data): rotated geometry primitives`

> Research log · [Phase 8 — Oriented detection, v0.3.0](https://borda.github.io/lucid-YOLO/research/phase-08-oriented-detection/)
>
> 🔗 Depends on [WP-054](https://borda.github.io/lucid-YOLO/work_packages/wp-054/) · Needed by [WP-056](https://borda.github.io/lucid-YOLO/work_packages/wp-056/), [WP-059](https://borda.github.io/lucid-YOLO/work_packages/wp-059/)
>
> ⬅️ [WP-054](https://borda.github.io/lucid-YOLO/work_packages/wp-054/) · [Roadmap](https://borda.github.io/lucid-YOLO/ROADMAP/) · [WP-056](https://borda.github.io/lucid-YOLO/work_packages/wp-056/) ➡️


## WP-055 — rotated geometry primitives

<a id="wp-055"></a>

Canonicalization is exactly idempotent rather than idempotent to tolerance, and the angle range is a guarantee rather than a near-certainty. The edge case that forces the final clamp: for an angle within an ulp of a range bound, shifting by multiples of pi cannot rescue it — the remainder rounds to pi and the shift returns its own input. A similarity round trip of a box at exactly −45° reaches this in practice, so a clamp closes it at a cost of one ulp.

The square tie-break keys on exact `w == h`, which means a quad reconstructed from DOTA's eight coordinates can return either pi/2-separated representative of the same rectangle. [WP-060](https://borda.github.io/lucid-YOLO/work_packages/wp-060/)'s angle loss, aimed precisely at near-square objects, must therefore not assume which — and that is why it is tested for bit-identical scoring of both representatives rather than agreement to seven digits.

