# Visual Angle Wrapping

## One picture
Angles work like a clock. After a full turn, you point in the same direction again.
No matter how many full rotations you add or subtract, the direction stays the same.
## Simple idea
Directions repeat every **360°** (or **2π radians**).
Examples:
- `450° = 90°` (one extra full turn)
- `810° = 90°` (two extra full turns)
- `-270° = 90°` (three-quarter turn backward)
- `90° + 720° = 90°` (two full turns forward)
## Programming idea
Use the modulo operator to keep rotation values bounded.
```python
def wrap_degrees(angle):
"""Wrap to [0, 360)"""
return angle % 360
def wrap_radians(angle):
"""Wrap to [0, 2π)"""
TAU = 6.283185307179586
return angle % TAU
```
For rotation where you want values centered on zero (e.g., -180 to +180):
```python
def wrap_signed_degrees(angle):
"""Wrap to (-180, 180]"""
return ((angle + 180) % 360) - 180
def wrap_signed_radians(angle):
"""Wrap to (-π, π]"""
PI = 3.141592653589793
TAU = 2 * PI
return ((angle + PI) % TAU) - PI
```
## Why it matters in games and simulations
A game object can rotate for minutes or hours, accumulating floating-point drift. Wrapping keeps the value bounded and prevents precision loss over time.
```python
# Bad: spacecraft.heading grows forever
spacecraft.heading += rotation_speed * dt
# Good: stays in [0, 360) always
spacecraft.heading = (spacecraft.heading + rotation_speed * dt) % 360
```
## Angle difference
Finding the shortest rotation between two angles is a common bug source:
```python
def shortest_angle_diff(a, b):
"""Returns the signed shortest rotation from a to b, in degrees."""
diff = (b - a + 180) % 360 - 180
return diff
# Example: from 350° to 10° should be +20°, not -340°
print(shortest_angle_diff(350, 10)) # 20.0
print(shortest_angle_diff(10, 350)) # -20.0
```
## Common mistakes
- Adding angles without wrapping, causing values like `3600°` or `100 * 2π`.
- Comparing unwrapped angles: `if heading == target` can fail when both represent the same direction but with different accumulated turns.
- Forgetting that `% 360` in Python always returns a positive value, but in some languages `%` returns negative for negative inputs.
## See also
- [Visual Degrees vs Radians](/articles/4bc70fd7-fa6e-4989-988f-834ff34babdf)
- [Visual Radians Without Math Libraries](/articles/7090a8d6-5f02-456e-82ec-31741bd1fe38)
- [Visual Inverse Trig and atan2 Without Math Libraries](/articles/85a06148-c2a9-429d-93e4-3b8ffcb93089)